Java线程与进程
进程与线程的关系
进程里面至少有一个线程,进程间的切换会有较大的开销
线程必须依附在进程上,同一进程共享代码和数据空间
多线程的优势
多线程可以达到高效并充分利用cpu
线程使用的方法
继承Thread类使用线程
public class Test{ public static void main(String[] args){ MyThread t = new MyThread("x1x"); t.start(); // 启动线程 } } // 自定义一个类,继承Thread类,重写run()方法 class MyThread extends Thread{ private Thread thread; private String threadName; public MyThread(String threadName){ this.threadName = threadName; } @override public void run(){ System.out.println("线程名称:" + threadName); for(int i = 0; i < 5; i++){ System.out.println("Thread:" + threadName + "-----" + i); } } public void start(){ if(null == thread){ thread = new Thread(this, threadName); thread.start(); } } }
继承Thread类
实现Runnable接口使用线程
public class Test{ public static void main(String[] args){ MyRunnable m = new MyRunnable("r1r"); m.start(); } } // 实现Runnable接口,实现run()方法 class MyRunnable implements Runnable{ private Thread thread; private String threadName; // 构造方法 public MyRunnable(String threadName){ this.threadName = threadName; } @override public void run(){ System.out.println("线程名称: " + threadName); for(int i = 0; i < 6; i++){ System.out.println("Thread: " + threadName + "----" + i); } } public void start(){ if(null == thread){ thread = new Thread(this, threadName); thread.start(); } } }
实现Runnable接口
实现Callable接口使用线程
public class MyCallable implements Callable<Integer>{ public static void main(String[] args){ MyCallable mc = new MyCallable(); // 这里的泛型里面填写的类型也与实现Callable时填写的一致 FutureTask<Integer> fTask = new FutureTask<>(mc); new Thread(fTask, "带返回值的线程").start(); System.out.println(fTask.get()); // 拿到返回值 } /* * call()的返回值类型和实现Callable时填写的类型一致,比如这里是Integer类型,那么 * call()方法的返回值类型就是Integer类型 */ @override public Integer call() throws Exception{ for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + "----" + i); } return i; } }
实现Callable接口
用匿名内部类的形式使用线程
public class Test{ public static void main(String[] args){ // 利用Thread类实现 new Thread(){ @override public void run(){ while(true){ System.out.println(Thread.currentThread().getName()); } } }.start(); // 利用Runnable接口实现 new Thread(new Runnable(){ @override public void run(){ while(true){ System.out.println(Thread.currentThread().getName()); } } }).start(); } }
内部类形式调用
继承Runnable和Callable接口比继承Thread类的优势
- 适合多个相同的程序代码的线程去处理同一个资源
- 可以避免java中的单继承的限制
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
线程的优先级
java中的线程是具有优先级的,优先级高的线程会获得更多的运行机会。
java中线程的优先级是用整数来表示的,取值范围是1 - 10
Thread类中有三个静态常量表示优先级:
static int MAX_PRIORITY 线程可以具有的最高优先级,取值为10
static int MIN_PRIORITY 线程可以具有的最低优先级,取值为1
static int NORM_PRIORITY 分配给线程的默认优先级,取值为5
如果要对线程设置优先级或者获取线程的优先级,可以使用Thread类的setPriority 和 getPriority方法分别得到和获取
线程的常用方法
sleep() 强迫线程睡眠
join() 等待线程终止
currentThread() 得到当前方法所在的线程
isDaemon() 一个线程是否为守护线程
setDaemon() 设置一个线程为守护线程
setName() 为线程设置名称
wait() 强迫线程等待
interrupt() 终止线程
notify() notifyAll() 通知线程继续执行
setPriority() 设置优先级
getPriority() 获取优先级
同步
使用synchronized同步锁
- 获得同步锁
- 清空工作内存
- 从主内存拷贝对象到工作内存
- 刷新主内存
- 释放同步锁
/* * 这个程序的本意是第一个线程先输出zhangsan,然后第二个线程输出lisi * 但是由于两个线程同步执行,第一个线程调用output()方法还未结束,第二个线程就已 *经开始调用output()方法 * 最终的输出就会是zlhiasnigsan * 就好比两个工人在同一时间使用同一机器加工零件,那不就乱套了吗? */ public class synchronizedTest { public static void main(String[] args) { final Outputter o = new Outputter(); new Thread() { public void run() { o.output("zhangsan"); } }.start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub o.output("lisi"); } }).start(); } } class Outputter{ // 放在代码块前面 public void output(String name) { for(int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
没有同步带来的问题
/* * 在要调用的output()方法前面加上synchronized同步锁就可以解决上述问题 * 当使用同步锁后,同一对象在同一时间只允许一个线程使用 * 也就是说,第一个线程调用output()方法,在进入方法后,synchronized就 * 上锁了,后面的线程就不能调用了,必须等待当前线程执行完output()方法释放锁后 * 后面的线程才允许调用 * 就好比第一个工人要开始使用机器加工零件了,在使用机器前,他将房门锁住了,于是 * 后面 * 的工人便不能进房间使用机器,必须等工人一加工完零件,将锁打开,第二个工人才能 * 进去 * 加工零件 */ public class synchronizedTest { public static void main(String[] args) { final Outputter o = new Outputter(); new Thread() { public void run() { o.output("zhangsan"); } }.start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub o.output("lisi"); } }).start(); } } class Outputter{ // 放在代码块前面 public synchronized void output(String name) { for(int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
使用同步解决问题1
/* * 第二种使用synchronized的方法就是将synchronized * 放在要上锁的代码块前面,原理与上述一致 */ public class synchronizedTest { public static void main(String[] args) { final Outputter o = new Outputter(); new Thread() { public void run() { o.output("zhangsan"); } }.start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub o.output("lisi"); } }).start(); } } class Outputter{ public void output(String name) { // 放在代码块前面 synchronized(this) { for(int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
使用同步锁解决问题2
synchronized(){
}的括号里面放的是需要互斥的对象,就是两个工人加工零件使用的同一个机器
使用volatile同步变量
volatile使变量对线程具有可见性