java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动
public class OnlyMain { public static void main(String[] args) { //虚拟机线程管理接口 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); for (ThreadInfo info : threadInfos) { System.out.printf("[%s] %s/n",info.getThreadId(),info.getThreadName()); } } } ////////////////////////////////控制台输出 [6] Monitor Ctrl-Break [5] Attach Listener [4] Signal Dispatcher [3] Finalizer [2] Reference Handler [1] main
实现callable接口
private static class ThreadClass extends Thread{ @Override public void run() { System.out.println("this is threadClass");; } } private static class RunnableClass implements Runnable{ @Override public void run() { System.out.println("this is runnableClass");; } } private static class CallableClass implements Callable<String>{ @Override public String call() throws Exception { System.out.println("this is callableClass");; return "callableClass"; } } public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadClass threadClass = new ThreadClass(); RunnableClass runnableClass = new RunnableClass(); CallableClass callableClass = new CallableClass(); // extends Thread start threadClass.start(); // implements runnable start new Thread(runnableClass).start(); // implements callable start FutureTask<String> futureTask = new FutureTask<>(callableClass); new Thread(futureTask).start(); System.out.println(futureTask.get());; } ////////////////////////////////控制台输出 this is threadClass this is runnableClass this is callableClass callableClass
interrupt() 中断一个线程,不是强制停止,通过协作的方式进行,将中断标志位置为true
isInterrupted() 判定当前线程是否处于中断状态
static interrupted() 判定当前线程是否处于中断状态,将中断标志位置为false
private static class ThreadClass extends Thread { @Override public void run() { String threadName = Thread.currentThread().getName(); while (!isInterrupted()) { System.out.println(threadName +" is run!"); } System.out.println(threadName + " flag is " + isInterrupted()); } } private static class RunnableClass implements Runnable { @Override public void run() { String threadName = Thread.currentThread().getName(); while (!Thread.currentThread().isInterrupted()) { System.out.println(threadName +" is run!"); } System.out.println(threadName + " flag is " + Thread.currentThread().isInterrupted()); } } public static void main(String[] args) throws InterruptedException { ThreadClass threadClass = new ThreadClass(); threadClass.start(); Thread.sleep(10); threadClass.interrupt(); RunnableClass runnableClass = new RunnableClass(); Thread runnableThread = new Thread(runnableClass,"runnableClass"); runnableThread.start(); Thread.sleep(10); runnableThread.interrupt(); }
warning:如果线程中有 InterruptedException
异常的话,这是因为 InterruptedException
会重置异常标志位为false,会对异常中断位有影响,下面程序重复运行几次就会产生这样的异常情况。解决的方法就是在catch块中添加 interrupt()
;
private static class ThreadClass extends Thread { @Override public void run() { String threadName = Thread.currentThread().getName(); while (!isInterrupted()) { try { Thread.sleep(100); } catch (InterruptedException e) { System.out.println(threadName + " flag1 is " + isInterrupted()); // interrupt(); e.printStackTrace(); } System.out.println(threadName + " flag2 is " + isInterrupted()); } System.out.println(threadName + " flag3 is " + isInterrupted()); } } public static void main(String[] args) throws InterruptedException { ThreadClass threadClass = new ThreadClass(); threadClass.start(); Thread.sleep(200); threadClass.interrupt(); } //////////////////////////异常结果 Thread-0 flag2 is false Thread-0 flag2 is false Thread-0 flag1 is false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.thread.demo.InterruptExceptionThread$ThreadClass.run(InterruptExceptionThread.java:16) Thread-0 flag2 is false Thread-0 flag2 is false //////////////////////////正常结果 Thread-0 flag2 is false Thread-0 flag2 is false Thread-0 flag3 is true
当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
在java中可以通过 setDaemon(true)
的方式将一个线程设置为守护线程
//读写操作 private static class WriteFileRunnable implements Runnable { @Override public void run() { try { File f = new File("daemon.txt"); FileOutputStream os = new FileOutputStream(f, true); os.write("daemon".getBytes()); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { WriteFileRunnable writeFileRunnable = new WriteFileRunnable(); Thread writeThread = new Thread(writeFileRunnable); writeThread.setDaemon(true); writeThread.start(); } //////////////////////////结果 //文件daemon.txt中没有"daemon"字符串。 //包含try...catch...finally private static class ThreadClass extends Thread { @Override public void run() { String threadName = Thread.currentThread().getName(); try { while (true) { System.out.println(threadName + " is run!"); } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("....................finally"); } } } public static void main(String[] args) throws InterruptedException { ThreadClass threadClass = new ThreadClass(); threadClass.setDaemon(true); threadClass.start(); Thread.sleep(10); } //////////////////////////结果 Thread-0 is run! Thread-0 is run! Thread-0 is run! Thread-0 is run! Process finished with exit code 0
在并发编程中存在线程安全问题,主要原因有: 存在共享数据 或 多线程共同操作共享数据 。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)。
synchronized分为 类锁
和 对象锁
,synchronized添加在方法中和使用synchronized(this)都是使用了对象锁,如果方法定义成静态的,再添加synchronized,此时锁为类锁。类锁和对象锁可以并行运行,不同对象的对象锁也是可以并行运行的。
public class SynchronizedDemo { private static class InstanceSyncMethod implements Runnable { static int tag = 1; //共享资源 public synchronized void increase() { tag++; } @Override public void run() { for (int i = 0; i < 1000; i++) { increase(); } } } public static void main(String[] args) throws InterruptedException { InstanceSyncMethod instance = new InstanceSyncMethod(); Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(InstanceSyncMethod.tag); } } //////////////////////////输出结果 2001
synchronized
用于实例对象的时候存在一个问题,当创建多个实例的时候,虽然方法上使用了 synchronized
,但是因为存在多个不同的实例对象锁,因此t都会进入各自的对象锁,也就是说多个线程使用的是不同的锁,因此线程安全是无法保证的。
//new新实例 Thread t1=new Thread(new InstanceSyncMethod()); //new新实例 Thread t2=new Thread(new InstanceSyncMethod()); t1.start(); t2.start(); //join含义:当前线程A等待thread线程终止之后才能从thread.join()返回 t1.join(); t2.join(); System.out.println(InstanceSyncMethod.tag); ////////////////////////////这个时候输出的结果就不确定是多少
解决这种困境的的方式是将synchronized作用于静态的方法,这样的话,对象锁就当前类对象,由于无论创建多少个实例对象,但对于的类对象拥有只有一个,所有在这样的情况下对象锁就是唯一的。
synchronized作用于静态方法时,其锁就是当前类的class对象锁。调用一个实例对象的非static synchronized方法,和静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
private static class InstanceSyncMethod implements Runnable { static int tag = 1; //共享资源 public static synchronized void increase() { tag++; } @Override public void run() { for (int i = 0; i < 1000; i++) { increase(); } } } public static void main(String[] args) throws InterruptedException { //new新实例 Thread t1 = new Thread(new InstanceSyncMethod()); //new新实例 Thread t2 = new Thread(new InstanceSyncMethod()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(InstanceSyncMethod.tag); } //////////////////////////输出结果 2001
在某些情况下,方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,就不建议直接对整个方法进行同步操作,此时可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作。
private static class InstanceSyncMethod implements Runnable { static int tag = 1; //共享资源 @Override public void run() { synchronized (this){ for (int i = 0; i < 1000; i++) { tag++; } } } } public static void main(String[] args) throws InterruptedException { //new新实例 Thread t1 = new Thread(new InstanceSyncMethod()); //new新实例 Thread t2 = new Thread(new InstanceSyncMethod()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(InstanceSyncMethod.tag); } //////////////////////////输出结果 2001 //////////////////////////类似的方法块 //this,当前实例对象锁 synchronized(this){ ... } //class对象锁 synchronized(XXX.class){ ... }
volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。
volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。在Java内存模型中,有main memory,每个线程也有自己的memory。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。
一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。
也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。
下面程序就是验证volatile不能保证线程执行的有序性。
private static class VolatileClass implements Runnable { private volatile int a = 1; @Override public void run() { String threadName = Thread.currentThread().getName(); a = a + 1; System.out.println(threadName +":=" + a); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } a = a + 1; System.out.println(threadName +":=" + a); } } public static void main(String[] args) { VolatileClass volatileClass = new VolatileClass(); Thread t1 = new Thread(volatileClass); Thread t2 = new Thread(volatileClass); Thread t3 = new Thread(volatileClass); Thread t4 = new Thread(volatileClass); t1.start(); t2.start(); t3.start(); t4.start(); } //////////////////////////输出结果 Thread-0:=2 Thread-3:=5 Thread-2:=4 Thread-1:=3 Thread-3:=7 Thread-1:=7 Thread-0:=7 Thread-2:=7
threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。
private static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>() { @Override public Integer initialValue() { return 1; } }; private static class ThreadLoaclClass implements Runnable { @Override public void run() { String threadName = Thread.currentThread().getName(); localNum.set(localNum.get() + 1); System.out.println(threadName +":=" + localNum.get()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } localNum.set(localNum.get() + 1); System.out.println(threadName +":=" + localNum.get()); } } public static void main(String[] args) throws InterruptedException { ThreadLoaclClass loaclClass = new ThreadLoaclClass(); Thread t1 = new Thread(loaclClass); Thread t2 = new Thread(loaclClass); Thread t3 = new Thread(loaclClass); Thread t4 = new Thread(loaclClass); t1.start(); t2.start(); t3.start(); t4.start(); } //////////////////////////输出结果 Thread-0:=2 Thread-3:=2 Thread-1:=2 Thread-2:=2 Thread-1:=3 Thread-2:=3 Thread-0:=3 Thread-3:=3
每个线程所产生的序号虽然都共享同一个实例,但它们并没有发生相互干扰的情况,而是各自产生独立的数字,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
ThreadLocal和Synchronized都能解决多线程中相同变量的访问冲突问题,不同的是