线程基本知识
1.开启多线程的两种方式
-
继承Thread类
-
实现Runnable接口
public class NewThread {
public static void main(String[] args) {
new Thread1().start();
new Thread(new Thread2()).start();
}
}
class Thread1 extends Thread {
JDK关于多线程的开启方式的说明
/* There are two ways to create a new thread of execution. One is to
* declare a class to be a subclass of <code>Thread</code>. This
* subclass should override the <code>run</code> method of class
* <code>Thread</code>. An instance of the subclass can then be
* allocated and started
....
* The other way to create a thread is to declare a class that
* implements the <code>Runnable</code> interface. That class then
* implements the <code>run</code> method. An instance of the class can
* then be allocated, passed as an argument when creating
* <code>Thread</code>, and started.
*/
2.守护线程
设置线程为守护线程之后,线程会在主线程结束后直接结束,所以finally
代码块将不再保证能执行
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
DaemonThread1 daemonThread1 = new DaemonThread1();
daemonThread1.setDaemon(true);
daemonThread1.start();
Thread.sleep(1);
daemonThread1.interrupt();
}
}
class DaemonThread1 extends Thread {
3.中断线程
安全中断线程interrupt()
,只是提示线程该中断了,并不保证一定中断
public class InterruptThread {
public static void main(String[] args) throws InterruptedException {
InterruptThread1 thread1 = new InterruptThread1();
Thread thread = new Thread(new InterruptThread2());
thread1.start();
thread.start();
Thread.sleep(1);
thread1.interrupt();
thread.interrupt();
Thread thread = new Thread(new InterruptThread3());
thread.start();
Thread.sleep(1);
thread.interrupt();
}
}
class InterruptThread1 extends Thread {
差异:
isInterrupted()
Thread.interrupted()
判断线程是否被中断,如果线程处于阻塞状态,线程在检查中断标示时如果发现中断标示为true,会抛异常,这个方法会将中断标志位重置为false
4.Sleep对Lock的影响
Sleep不会释放锁
public class ThreadSleepOnLock {
private static final Object lock = new Object();
public static void main(String[] args) {
new ThreadSleepOnLock1().start();
new ThreadSleepOnLock2().start();
}
private static class ThreadSleepOnLock1 extends Thread {
5.Join
使用join()
进行插队,保证线程执行的有序性
public class ThreadJoin {
public static void main(String[] args) {
Join2 join2 = new Join2();
join2.setName("Join2");
Join1 join1 = new Join1(join2);
join1.setName("Join1");
join1.start();
join2.start();
}
}
class Join1 extends Thread {
private Thread sub;
public Join1(Thread sub) {
this.sub = sub;
}
public Join1() {
}
线程间共享与协作
1.线程间共享
-
synchronized 关键字
确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制
public class SynchronizedTest {
private int count = 0;
private static final Object lock = new Object();
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedTest synchronizedTest = new SynchronizedTest();
SynchronizedThread thread1 = synchronizedTest.new SynchronizedThread();
SynchronizedThread thread2 = synchronizedTest.new SynchronizedThread();
SynchronizedThread thread3 = synchronizedTest.new SynchronizedThread();
thread1.setName("Thread1");
thread2.setName("Thread2");
thread3.setName("Thread3");
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(2000);
System.out.println(synchronizedTest.getCount());
}
private class SynchronizedThread extends Thread {
对象锁 锁的是对象,一个class可以有多个对象,锁不同的对象,互不干扰
类锁 加在静态方法上的锁,锁的是整个class类,只有一份
public class SynchronizedThread {
public static void main(String[] args) {
InstanceThread instanceThread1 = new InstanceThread();
InstanceThread instanceThread2 = new InstanceThread();
InstanceThread instanceThread3 = new InstanceThread();
instanceThread1.setName("Thread1");
instanceThread2.setName("Thread2");
instanceThread3.setName("Thread3");
instanceThread1.start();
instanceThread2.start();
instanceThread3.start();
}
}
class InstanceThread extends Thread{
错误的加锁会导致不可预知的结果
public class ErrorSynchronized {
public static void main(String[] args) throws InterruptedException {
ErrorSynchronized errorSynchronized = new ErrorSynchronized();
ErrorSynchronizedThread thread1 = errorSynchronized.new ErrorSynchronizedThread();
for (int i = 0; i < 500; i++) {
new Thread(thread1).start();
}
System.out.println(thread1.count);
}
private class ErrorSynchronizedThread implements Runnable{
private Integer count=0;
错误分析:
反编译class
class ErrorSynchronized$ErrorSynchronizedThread
implements Runnable
{
private Integer count = Integer.valueOf(0);
private ErrorSynchronized$ErrorSynchronizedThread(ErrorSynchronized paramErrorSynchronized) {}
public void run()
{
Integer localInteger1;
synchronized (this.count)
{
localInteger1 = this.count;Integer localInteger2 = this.count = Integer.valueOf(this.count.intValue() + 1);
}
}
}
//jdk
//Integer.valueOf():超过±128将会new新对象,导致锁的是不同的对象,也就是没锁住
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
-
volatile 最轻量的同步
volatile
关键字是最轻量的同步机制
,保证了不同线程对某个变量进行操作时的可见性,但是不保证线程安全
,例如,可以使用在创建单例锁类对象时,防止二重锁.应用场景:一写多读public class VolatileThread {
private volatile static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
VolatileThread1 thread = new VolatileThread1();
thread.setName("Thread"+i);
thread.start();
}
Thread.sleep(2000);
flag=true;
}
public static class VolatileThread1 extends Thread{
volatile为什么不安全?
如果有一个变量i = 0用volatile修饰,两个线程对其进行i++操作,如果线程1从内存中读取i=0进了缓存,然后把数据读入寄存器,之后时间片用完了,然后线程2也从内存中读取i进缓存,因为线程1还未执行写操作,内存屏障是插入在写操作之后的指令,意味着还未触发这个指令,所以缓存行是不会失效的。然后线程2执行完毕,内存中i=1,然后线程1又开始执行,然后将数据写回缓存再写回内存,结果还是1
https://blog.csdn.net/qq_33330687/article/details/80990729
public class VolatileUnSafe {
private volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
VolatileUnSafe volatileUnSafe = new VolatileUnSafe();
int index=0;
while (index<10){
volatileUnSafe.new VolatileUnSafeThread().start();
index++;
}
Thread.sleep(1000);
System.out.println(volatileUnSafe.count);
}
class VolatileUnSafeThread extends Thread {
2.ThreadLocal
ThreadLocal
主要用于线程的隔离,Spring事务中,一个事务可能包含了多个业务逻辑,穿梭于多个Dao,所以回滚是应该是同一个Connection
,如果将Connection
保存到ThreadLocal
中,将十分有效,否则将需要将Connection
进行传递,比较繁琐
public class ThreadLocalUse {
private ThreadLocal<Integer> threadLocal=new InheritableThreadLocal<Integer>(){
ThreadLocal
应该要保存自己的变量,所以,变量不能定义为static
,否则会出现错误的结果
ThreadLocal
使用不当会引发内存泄漏
(该回收的对象没有得到回收,一直占用内存)Rather than keep track of all ThreadLocals, you could clear them all at once
protected void afterExecute(Runnable r, Throwable t) { // you need to set this field via reflection. Thread.currentThread().threadLocals = null; }As a principle, whoever put something in thread local should be responsible to clear it
//set了之后记得remove threadLocal.set(...); try { ... } finally { threadLocal.remove(); }
3.线程间协作
-
等待/通知
是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作
notify()
:通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。
notifyAll()
:通知所有等待在该对象上的线程
wait()
:调用该方法的线程进入 WAITING状态,只有等待另外线程的通知或被中断才会返回.需要注意,调用wait()方法后,会释放对象的锁
wait(long)
:超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回
wait (long,int)
对于超时时间更细粒度的控制,可以达到纳秒
-
等待和通知的标准范式
等待方遵循如下原则。
1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。
通知方遵循如下原则。
1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。
//等待方
sychronized(obj){
while(!condition){
obj.wait();
}
}
//通知方
sychronized(obj){
condtion=true;
obj.notify();
//or
obj.notifyAll();
}
永远在
while
循环而不是if
语句中使用wait()对在多线程间共享的那个Object来使用wait()
在调用wait(),notify()系列方法之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait(),notify()系列方法
尽量使用notifyAll(),而不是 notify()
public class WaitAndNotify {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new ConsumerThread().start();
}
Thread.sleep(1000);
new ProducerThread().start();
}
}
class Shop {
public static final List<String> PRODUCTS = new ArrayList<String>();
}
class ProducerThread extends Thread {