都知道,比如Java做那种卖票的,比如卖10张票,如果没加锁,那么每一个线程都是卖10张票,这不合理
多线程执行同一个东西,没加锁,就有有错误
package restudy;
public class MyRunnable implements Runnable {
int ticket = 20;
public void run() {
while (true) {
sale();
}
}
public void sale() {
if (ticket > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "------" + ticket);
ticket--;
}
}
}
package restudy;
public class demo1 {
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread mt1 = new Thread(run);
Thread mt2 = new Thread(run);
Thread mt3 = new Thread(run);
mt1.start();
mt2.start();
mt3.start();
}
}
Thread-1------20
Thread-0------20
Thread-2------18
Thread-0------17
Thread-1------17
Thread-2------17
Thread-1------14
Thread-2------14
Thread-0------14
Thread-1------11
Thread-2------11
Thread-0------11
Thread-2------8
Thread-1------8
Thread-0------8
Thread-1------5
Thread-2------5
Thread-0------5
Thread-0------2
Thread-1------2
Thread-2------2
Thread-1-------1
这很明显是不合理的,有些这个线程执行过了,那个线程还执行一遍
synchronized(同步锁){
需要同步操作的代码
}
注意:
思路:一个线程里面,初定义一个 对象,可以是任意对象,用synchronized锁住该对象,这样多个线程一起执行的时候,就会用到同一个对象锁住
package restudy;
public class MyRunnable implements Runnable {
int ticket = 20;
Object lock = new Object();
public void run() {
while (true) {
//所住一个方法
synchronized (lock) {
sale();
}
}
}
public void sale() {
if (ticket > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "------" + ticket);
ticket--;
}
}
}
package restudy;
public class demo1 {
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread mt1 = new Thread(run);
Thread mt2 = new Thread(run);
Thread mt3 = new Thread(run);
mt1.start();
mt2.start();
mt3.start();
}
}
Thread-0------20
Thread-2------19
Thread-2------18
Thread-1------17
Thread-2------16
Thread-0------15
Thread-2------14
Thread-1------13
Thread-1------12
Thread-2------11
Thread-2------10
Thread-0------9
Thread-2------8
Thread-1------7
Thread-2------6
Thread-0------5
Thread-2------4
Thread-1------3
Thread-2------2
Thread-0------1
public synchronized void 方法名(){
可能会产生线程安全问题的代码
}
package restudy;
public class MyRunnable implements Runnable {
int ticket = 20;
public void run() {
while (true) {
sale();
}
}
//方法添加锁
public synchronized void sale() {
if (ticket > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "------" + ticket);
ticket--;
}
}
}
package restudy;
public class demo1 {
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread mt1 = new Thread(run);
Thread mt2 = new Thread(run);
Thread mt3 = new Thread(run);
mt1.start();
mt2.start();
mt3.start();
}
}
Thread-0------20
Thread-0------19
Thread-2------18
Thread-1------17
Thread-2------16
Thread-0------15
Thread-2------14
Thread-2------13
Thread-1------12
Thread-2------11
Thread-0------10
Thread-2------9
Thread-1------8
Thread-2------7
Thread-0------6
Thread-2------5
Thread-1------4
Thread-2------3
Thread-0------2
Thread-2------1
使用步骤:
前
调用lock()
方法后
调用unlock()
方法package restudy;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyRunnable implements Runnable {
int ticket = 20;
Lock l=new ReentrantLock();//创建Lock对象
public void run() {
while (true) {
l.lock();//加锁
sale();//执行代码
l.unlock();//释放锁
}
}
public void sale() {
if (ticket > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "------" + ticket);
ticket--;
}
}
}
package restudy;
public class demo1 {
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread mt1 = new Thread(run);
Thread mt2 = new Thread(run);
Thread mt3 = new Thread(run);
mt1.start();
mt2.start();
mt3.start();
}
}
Thread-0------20
Thread-1------19
Thread-2------18
Thread-0------17
Thread-1------16
Thread-2------15
Thread-0------14
Thread-1------13
Thread-2------12
Thread-0------11
Thread-1------10
Thread-2------9
Thread-0------8
Thread-0------7
Thread-1------6
Thread-2------5
Thread-0------4
Thread-1------3
Thread-2------2
Thread-0------1
上面的那些,都是几个线程之间互相争夺CPU线程,谁抢到谁运行的,这类似于斗争,但除了这种情况,还有合作的机制。
就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将 其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
wait/notify 就是线程间的一种协作机制。
synchronized
例子:创建一个
ObjectTest类 ObjectTest类,属性有flag。当flag为假时,WaitTest休眠,等待NotifyTes将其唤醒。当当flag为真时,NotifyTes休眠,等待WaitTest将其唤醒
package restudy;
public class ObjectTest {
//假设我这个一开始就设定为假的了,这个一开始要有值,赋值的形式很多就不细说了
boolean flag=false;
}
WaitTest类
package restudy;
public class WaitTest extends Thread {
private ObjectTest obj;
public WaitTest(ObjectTest obj) {
super();
this.obj = obj;
}
@Override
public void run() {
// 不断循环
while (true) {
// 这里为什么要加锁并且锁obj?
// 因为一开始说了,休眠与唤醒必须是同一个对象,那么WaitTest类和NotifyTest类必须要用同一个对象,这个对象就是ObjectTest
synchronized (obj) {
// 当ObjectTest对象为false时,WaitTest进行休眠
if (obj.flag == false) {
System.out.println(this.getClass().getName() + " " + "准备开始休眠");
try {
obj.wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
// 一下代码是唤醒的时候执行的
// 当代码执行到这里的时候,ObjectTest已经被唤醒了
else {
System.out.println(this.getClass().getName() + " " + "被唤醒");
obj.flag = false;
obj.notify();
}
}
}
}
}
NotifyTest类
package restudy;
public class NotifyTest extends Thread {
private ObjectTest obj;
public NotifyTest(ObjectTest obj) {
super();
this.obj = obj;
}
@Override
public void run() {
// 不断循环
while (true) {
synchronized (obj) {
if (obj.flag == true) {
try {
obj.wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
//代码执行到这里的时候,说明flag=false并且准备讲WaitTest唤醒
//WaitTest休眠了,NotifyTest就趁虚而入
else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(this.getClass().getName() + "准备唤醒另一个程序");
obj.flag = true;
obj.notify();
}
}
}
}
}
主程序
package restudy;
public class demo1 {
public static void main(String[] args) {
ObjectTest obj=new ObjectTest();
WaitTest w=new WaitTest(obj);
NotifyTest n=new NotifyTest(obj);
w.start();
n.start();
}
}
代码执行流程:
主程序,创建3个类,然后start,。首先,走到WaitTest对象,锁obj,判断obj.flag为false,输出准备开始休眠,WaitTest
进行了休眠,这个时候WaitTest
不再争夺CPU,由于两个类都是不断循环的,WaitTest
休眠了,NotifyTest
乘虚而入,判断flag为flase,不符合NotifyTest
的休眠条件,NotifyTest
进行语句,其从29行开始,睡眠0.5秒,输出准备唤醒另一个程序,改变flag的值为true,obj.notify();
注意NotifyTest
的38行obj.notify();
,唤醒有用到obj
的程序,WaitTest
唤醒了。这时候flag==true
两个程序判断flag值,NotifyTest
休眠,WaitTest
判断值不为flag,进行31行的代码,输出被唤醒,将flag变为false,唤醒有用到obj
的程序,NotifyTest
醒了
又是一次新的轮回,这时候flag为false,WaitTest
休眠,NotifyTest
活动…
从上面的例子看到,每要用一个线程就要创建一个线程,这在开发中是很慢的,就产生了一个叫线程池的东西
一开始就创建好线程池,要用就拿出来,用完了(run()完)不要释放,丢回去线程池,所以线程池是一直存在的
public static ExecutorService newFixedThreadPool(int nThreads)
:返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)。返回值:ExecutorService接口Future> submit(Runnable task)
:提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。 参数:task - 要提交的任务。返回:表示任务等待完成的 FutureExecutors.newFixedThreadPool(int 几个线程容量)
),返回集为ExecutorService
。submi()
方法,传递线程任务,开启线程,执行run方法shutdown()
)。package restudy;
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了一个新的线程");
}
}
package restudy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo1 {
public static void main(String[] args) {
// 创建一个容量为2的线程池,返回为ExecutorService
ExecutorService ex = Executors.newFixedThreadPool(2);
//传递线程任务,开启线程,执行run方法
ex.submit(new RunnableImpl());
ex.submit(new RunnableImpl());
ex.submit(new RunnableImpl());
ex.submit(new RunnableImpl());
}
}
pool-1-thread-2创建了一个新的线程
pool-1-thread-2创建了一个新的线程
pool-1-thread-1创建了一个新的线程
pool-1-thread-2创建了一个新的线程
第一次ex.submit(new RunnableImpl());
,用了线程池里面的随机一个线程来执行,run()只包含了一个输出语句,run完后就结束了,结束之后就把该线程丢回线程池,等待下一次线程的产生。第二次submit
,又随机调用一个线程池里面的线程来进行run()…