总结:
多线程创建的4中方式:
Thread Runnable Callable 线程池
加锁的方式:
synchronized
ReentrantLock lock unlock
生产者消费者模式,线程的等待唤醒通知机制:
(用while防止虚假唤醒)
synchronized wait() notifyall()
ReentrantLock Condition的await() 和signalAll()
线程的锁机制:
static synchronized 和 synchronized
所有的
静态同步方法用的是同一把锁,类对象本身。
所有静态同步方法用的是同一把锁,实例对象本身
线程轮询的时候定制顺序访问:
condition1.await() condition2.signal()
读写锁:ReentrantReadWriteLock
ReentrantReadWriteLock
rw
=
new
ReentrantReadWriteLock();
rw
.writeLock().lock();
rw
.writeLock().unlock();
rw
.readLock().lock();
rw
.readLock().lock();
Lock的模板:
lock.lock();
try {
//判断、干活、唤醒
} finally {
lock.unlock();
}
线程池: 利用Executors得到ExecutorService
Executors
.
newFixedThreadPool(5) //5个线程的线程池
Executors.newSingleThreadExecutor()//一个线程
Executors.
newCachedThreadPool
()//随机线程
Executors.
newScheduledThreadPool
(5)//5个线程,有时间间隔
多线程之间按顺序调用 A
->
B
->
C
*AA打印5次,BB打印10次,CC打印15次
*接着
*AA打印5次,BB打印10次,CC打印15次
* ......来10轮
分析:三个AA,BB,CC线程都进行10次循环,线程i=1先抢到,number=1先进入AA线程的方法中,当AA循环打印了5次后,利用condition2.signal()唤醒了condition2.await()的
1.售票问题:
class
Tikect {
// 高内聚,低耦合
int
number
= 30;
ReentrantLock
lock
=
new
ReentrantLock();
public
void
sale() {
// 票具有的方法
lock
.lock();
try
{
if
(
number
>0){
System.
out
.println(Thread.
currentThread
().getName() +
"\t还剩"
+
number
+
"\t卖出第"
+(--
number
));
}
}
catch
(Exception
e
) {
}
finally
{
lock
.unlock();
}
}
}
/**
*
* 三个售票员 三个窗口 卖30张票 多线程问题
*
* 线程 解决 资源类 高内聚,低耦合
*
*
*
*
@author
77585211
*
*/
public
class
Test1 {
public
static
void
main(String[]
args
) {
Tikect
tikect
=
new
Tikect();
new
Thread(
new
Runnable() {
@Override
public
void
run() {
for
(
int
i
= 0;
i
< 50;
i
++) {
tikect
.sale();
}
}
},
"售票員A"
).start();
new
Thread(
new
Runnable() {
@Override
public
void
run() {
for
(
int
i
= 0;
i
< 50;
i
++) {
tikect
.sale();
}
}
},
"售票員B"
).start();
new
Thread(
new
Runnable() {
@Override
public
void
run() {
for
(
int
i
= 0;
i
< 50;
i
++) {
tikect
.sale();
}
}
},
"售票員C"
).start();
}
}
总结1.:ReentrantLock比Synconized好的地方是,Syconized是锁掉整个方法,而ReentrantLock是锁住几行。
lock.lock()方法要放在try..catch之前
2.创建多线程的方法之一 : 实现Callable接口
/**
* 根据
Callable
<V>
接口创建多线程
*
@author
77585211
*
*
*当有一件事情特别浪费时间,可以用FutureTask,用于异步获取结果
*线程 操作 资料类
*高内聚 低耦合
*
*/
public
class
Test2 {
public
static
void
main(String[]
args
)
throws
InterruptedException, ExecutionException{
//主方法中不写逻辑
FutureTask<Integer>
ft
=
new
FutureTask<Integer>(
new
Call());
new
Thread(
ft
,
"AA"
).start();
System.
out
.println(
"*********main主方法1"
);
int
result
=
ft
.get();
System.
out
.println(
"调用FutureTask中的方法:"
+
result
);
}
}
class
Call
implements
Callable<Integer>{
@Override
public
Integer call()
throws
Exception {
System.
out
.println(
"*****call()调用了"
);
Thread.
sleep
(2000);
return
123;
}
}
总结2: FutureTask<V>
1.多用于耗时的计算,主线程执行完之后,再去取结果
2.只计算一次
3.仅在计算完后才能得到结果,如果计算尚未完成就取数据,将会线程堵塞状态
3.多线程实现交替(一个减,一个加)
题目:现在有两个线程,可以操作初始值为0的数,实现一个线程对该变量+1,一个线程对该变量-1, 实现交替5轮
package
TreadTest1;
/**
* 现在有两个线程,可以操作初始值为0的数,实现一个线程对该变量+1,一个线程对该变量
-
1, 实现交替5轮
*
*
@author
77585211 线程 操作 资料类
*/
class
MyThread01 {
public
int
number
= 0;
// 两个方法, +1 ,-1
public
synchronized
void
increment()
throws
InterruptedException {
// 判断
// if(number!=0)会出现虚假判断
while
(
number
!= 0) {
this
.wait();
}
// 干活
++
number
;
System.
out
.println(Thread.
currentThread
().getName() +
"\t"
+
number
);
// 释放
this
.notifyAll();
}
public
synchronized
void
decrement()
throws
InterruptedException {
// 判断
// if(number!=0)会出现虚假判断
while
(
number
== 0) {
this
.wait();
}
// 干活
--
number
;
System.
out
.println(Thread.
currentThread
().getName() +
"\t"
+
number
);
// 释放
this
.notifyAll();
}
}
public
class
Test4 {
public
static
void
main(String[]
args
){
MyThread01
myThread01
=
new
MyThread01();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=5;
i
++) {
myThread01
.increment();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=5;
i
++) {
myThread01
.decrement();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"BB"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=5;
i
++) {
myThread01
.increment();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"CC"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=5;
i
++) {
myThread01
.decrement();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"DD"
).start();
}
}
优化: 使用JUC 的锁 Condition接口
package
TreadTest1;
import
java.util.concurrent.locks.Condition;
import
java.util.concurrent.locks.ReentrantLock;
/**
* 现在有两个线程,可以操作初始值为0的数,实现一个线程对该变量+1,一个线程对该变量
-
1, 实现交替5轮 使用ReentrantLock的方法
*
*
@author
77585211
*
*/
class
MyThread02 {
private
int
number
= 0;
private
ReentrantLock
lock
=
new
ReentrantLock();
private
Condition
condition
=
lock
.newCondition();
public
void
increment()
throws
InterruptedException {
lock
.lock();
// 线程一个一个的进
try
{
while
(
number
!= 0) {
condition
.await();
}
// 干活
++
number
;
System.
out
.println(Thread.
currentThread
().getName() +
"\t"
+
number
);
// 唤醒
condition
.signalAll();
}
finally
{
lock
.unlock();
}
}
public
void
decrement()
throws
InterruptedException {
lock
.lock();
// 线程一个一个的进
try
{
while
(
number
== 0) {
condition
.await();
}
// 干活
--
number
;
System.
out
.println(Thread.
currentThread
().getName() +
"\t"
+
number
);
// 唤醒
condition
.signalAll();
}
finally
{
lock
.unlock();
}
}
}
public
class
Test5 {
public
static
void
main(String[]
args
){
MyThread02
thread02
=
new
MyThread02();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=10;
i
++) {
thread02
.increment();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=10;
i
++) {
thread02
.decrement();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"BB"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=10;
i
++) {
thread02
.increment();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"CC"
).start();
new
Thread(()->{
try
{
for
(
int
i
= 1;
i
<=10;
i
++) {
thread02
.decrement();
}
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"DD"
).start();
}
}
重点:多线程的锁
1.都是同步方法,一个对象实例的时候
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
*
*
*
*
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone
=
new
Phone();
new
Thread(()->{
try
{
phone
.getIphone();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone
.getAndroid();
},
"BB"
).start();
}
}
结果:
---------iphone
---------Android
原因:
如果一个类中有多个
synchronized(),
只要有一个线程进入了一个
synchronized方法,其他线程都要等待。
因为synchronized是锁整个this.Object,被锁定后,其他线程不能进入
synchronized方法
2.
两个同步方法,两个对象的时候
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
---------
iphone
---------
Android
* 2.两个同步方法,两个对象的时候
----------
》
---------
Android
---------
iphone
*
*
*
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone1
=
new
Phone();
Phone
phone2
=
new
Phone();
new
Thread(()->{
try
{
phone1
.getIphone();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone2
.getAndroid();
},
"BB"
).start();
}
}
结果:
---------
Android
---------
iphone
结论:synchronized只会锁this.object。 不会影响其他的对象实例
3.
两个同步方法,一个普通方法,一个对象
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
public
void
sayHello(){
System.
out
.println(
"--------hello"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
---------
iphone
---------
Android
* 2.两个同步方法,两个对象的时候
----------
》
---------
Android
---------
iphone
* 3.两个同步方法,一个普通方法,一个对象
-->----
hello
-----
iphone
-----
Android
*
*
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone1
=
new
Phone();
Phone
phone2
=
new
Phone();
new
Thread(()->{
try
{
phone1
.getIphone();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone1
.getAndroid();
},
"BB"
).start();
new
Thread(()->{
phone1
.sayHello();
},
"CC"
).start();
}
}
结论:
--------hello
---------iphone
---------Android
3.结论 没有同步锁的方法不会被锁住,只有加了synchronized的方法才能锁住当前线程。
4.
两个静态同步方法,一个对象
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
static
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
static
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
public
void
sayHello(){
System.
out
.println(
"--------hello"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
---------
iphone
---------
Android
* 2.两个同步方法,两个对象的时候
----------
》
---------
Android
---------
iphone
* 3.两个同步方法,一个普通方法,一个对象
-->----
hello
-----
iphone
-----
Android
* 4.两个静态同步方法,一个对象
----
》
---------
iphone
---------
Android
*
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone1
=
new
Phone();
Phone
phone2
=
new
Phone();
new
Thread(()->{
try
{
phone1
.
getIphone
()
;
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone1
.
getAndroid
()
;
},
"BB"
).start();
}
}
结果:
---------iphone
---------Android
结论:所有静态同步方法用的锁是:类对象本身。
5.
两个静态同步方法,两个对象
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
static
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
static
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
public
void
sayHello(){
System.
out
.println(
"--------hello"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
---------
iphone
---------
Android
* 2.两个同步方法,两个对象的时候
----------
》
---------
Android
---------
iphone
* 3.两个同步方法,一个普通方法,一个对象
-->----
hello
-----
iphone
-----
Android
* 4.两个静态同步方法,一个对象
----
》
---------
iphone
---------
Android
* 5.两个静态同步方法,两个对象
---
》
---------
iphone
---------
Android
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone1
=
new
Phone();
Phone
phone2
=
new
Phone();
new
Thread(()->{
try
{
phone1
.
getIphone
()
;
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone2
.
getAndroid
()
;
},
"BB"
).start();
}
}
结果:
---------iphone
---------Android
结论: 静态同步方法不管是多少个对象实例,只要是一个类对象,当一个静态同步方法获取锁后,其他的静态同步方法必须等待。
6.
一个静态同步方法,一个非静态同步方法,一个对象
package
TreadTest1;
import
java.util.concurrent.TimeUnit;
class
Phone{
public
static
synchronized
void
getIphone()
throws
InterruptedException{
TimeUnit.
SECONDS
.sleep(3);
System.
out
.println(
"---------iphone"
);
}
public
synchronized
void
getAndroid(){
System.
out
.println(
"---------Android"
);
}
public
void
sayHello(){
System.
out
.println(
"--------hello"
);
}
}
/**
* 1.两个同步方法,一个对象的时候
----------
》
---------
iphone
---------
Android
* 2.两个同步方法,两个对象的时候
----------
》
---------
Android
---------
iphone
* 3.两个同步方法,一个普通方法,一个对象
-->----
hello
-----
iphone
-----
Android
* 4.两个静态同步方法,一个对象
----
》
---------
iphone
---------
Android
* 5.两个静态同步方法,两个对象
---
》
---------
iphone
---------
Android
* 6.一个静态同步方法,一个非静态同步方法,一个对象
---------
Android
---------
iphone
*
@author
77585211
*
*/
public
class
Test6 {
public
static
void
main(String[]
args
){
Phone
phone1
=
new
Phone();
Phone
phone2
=
new
Phone();
new
Thread(()->{
try
{
phone1
.
getIphone
()
;
}
catch
(Exception
e
) {
e
.printStackTrace();
}
},
"AA"
).start();
new
Thread(()->{
phone1
.getAndroid();
},
"BB"
).start();
}
}
结果:
---------
Android
-
--------
iphone
结论:静态同步方法锁住的是类对象,而非静态同步方法锁住的是当前对象实例。锁住的不是一个对象,所以syn的没有sleep的先被打印出来。
7:
一个静态同步方法,一个非静态同步方法,二个对象
结果:
---------Android
---------iphone
结论:与6相同
创建多线程的方法4:线程池
题:
银行窗口办理业务 5个窗口 15个人办理业务
package TreadTest1;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 根据线程池创建多线程
* @author 77585211
*
*银行窗口办理业务 5个窗口 15个人办理业务
*利用线程池
*/
public class Test3 {
public static void main(String[] args) throws InterruptedException, ExecutionException{
//实现2秒钟一个,时间调度
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);//5个线程池
ScheduledFuture<Integer> schedule =null;
//15个人办理业务
try {
for (int i = 1; i <=15; i++) {
schedule = service.schedule(()->{
//打印一下当前线程的名字
System.out.print(Thread.currentThread().getName());
//返回一个随机数
return new Random().nextInt(30);
}, 2, TimeUnit.SECONDS);
System.out.println("-------rerult:"+schedule.get());
}
} finally{
service.shutdown();
}
}
public static void getExecuor() throws Exception{
//5个窗口
//ExecutorService service = Executors.newFixedThreadPool(5);
//ExecutorService service = Executors.newSingleThreadExecutor();一个线程
ExecutorService service = Executors.newCachedThreadPool();//随机的线程池
Future<Integer> result=null;
//15个人办理业务
try {
for (int i = 1; i <=15; i++) {
result = (Future<Integer>) service.submit(()->{
//打印一下当前线程的名字
System.out.print(Thread.currentThread().getName());
//返回一个随机数
return new Random().nextInt(30);
});
//打印随机数
System.out.println("----result:"+result.get());
}
} finally{
service.shutdown();
}
}
}
总结:当窗口固定,且要重复提交业务的时候,用线程池