多線程與并發(fā)
多線程
線程的實(shí)現(xiàn)方法
繼承Thread類
- 繼承Thread類
- 重寫run方法
- start開啟線程
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->haha");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("thread1");
myThread.start();
}
}
實(shí)現(xiàn)Runnable接口
- 實(shí)現(xiàn)Runnable接口
- 創(chuàng)建Thread對象
- 參數(shù)為實(shí)現(xiàn)類
- start啟動線程
public class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("hahaha");
}
public static void main(String[] args) {
new Thread(new MyThread2()).start();
}
}
實(shí)現(xiàn)Callable接口
//1. 實(shí)現(xiàn)Callable谭确,返回值類型為String
public class Thread3 implements Callable<String> {
@Override
// 2. 重寫call方法
public String call() throws Exception {
System.out.println("hahahah");
return "我是返回值";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//3. 打開服務(wù),參數(shù)為服務(wù)個數(shù)
ExecutorService service = Executors.newFixedThreadPool(1);
//4. 提交執(zhí)行
Future<String> future = service.submit(new Thread3());
//5. 獲取返回值
String s = future.get();
//6. 結(jié)束服務(wù)
service.shutdownNow();
}
}
靜態(tài)代理模式
案例:
public class StaticProxy {
//************測試主方法*************
public static void main(String[] args) {
You you = new You();
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.marry();
}
}
//*************************
//結(jié)婚接口
interface Marry {
//結(jié)婚方法
void marry();
}
//*************************
//真實(shí)對象實(shí)現(xiàn)結(jié)婚接口
class You implements Marry {
//重寫結(jié)婚方法
@Override
public void marry() {
System.out.println("要結(jié)婚了,哈哈哈");
}
}
//*************************
//代理對象實(shí)現(xiàn)結(jié)婚接口
class WeddingCompany implements Marry {
private Marry target;
//通過傳入?yún)?shù)的方式調(diào)用真實(shí)對象
public WeddingCompany(Marry target) {
this.target = target;
}
//重寫結(jié)婚方法
@Override
public void marry() {
before();
this.target.marry();
after();
}
private void after() {
System.out.println("收尾款");
}
private void before() {
System.out.println("布置現(xiàn)場");
}
}
核心點(diǎn):
直接使用代理對象即可,為什么代理對象可以擁有真實(shí)對象的方法我纪,因?yàn)閚ew代理對象時,傳參傳了個真實(shí)對象<跸浮流济!
線程狀態(tài)
停止線程
- 不推薦使用JDK提供的stop() destory()廢棄方法。
- 可以使用flag=false讓線程停止
public class Thread4 implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i=0;
while (flag) {
System.out.println("thread4...."+i++);
}
}
public void myStop() {
this.flag = false;
}
//測試類
static class Test{
public static void main(String[] args) {
Thread4 thread4 = new Thread4();
new Thread(thread4).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
if (i==800){
thread4.myStop();
}
}
}
}
}
線程休眠sleep(抱著鎖睡覺)
sleep不釋放鎖技即,其他線程必須等待它執(zhí)行完畢才能執(zhí)行
public class ThreadDemo {
//lock對象將傳入synchronized代碼塊里著洼,為保證兩個線程搶一把鎖
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (lock){
try {
System.out.println("A休眠10秒不放棄鎖");
Thread.sleep(10000);
System.out.println("A休眠10秒醒來");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
synchronized (lock){
System.out.println("B休眠10秒不放棄鎖");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B休眠10秒醒來");
}
}).start();
}
}
A休眠10秒不放棄鎖
A休眠10秒醒來
B休眠10秒不放棄鎖
B休眠10秒醒來
或者
B休眠10秒不放棄鎖
B休眠10秒醒來
A休眠10秒不放棄鎖
A休眠10秒醒來
線程禮讓yield
public static void main(String[] args) {
new Thread(() -> {
System.out.println("A在執(zhí)行。而叼。身笤。");
Thread.yield();
System.out.println("A結(jié)束了");
}).start();
new Thread(() -> {
System.out.println("B在執(zhí)行");
Thread.yield();
System.out.println("B結(jié)束了");
}).start();
}
A在執(zhí)行
A結(jié)束了
B在執(zhí)行
B結(jié)束了
或者
A在執(zhí)行
B在執(zhí)行
A結(jié)束了
B結(jié)束了
線程插隊join
**注意 **:誰調(diào)用讓誰插隊
public static void main(String[] args) {
Thread threadA=new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("A在執(zhí)行==》"+i);
if (i==50){
}
}
});
threadA.start();
Thread threadB=new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("B在執(zhí)行==》"+i);
if (i==50){
try {
// 讓A線程插隊
threadA.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
threadB.start();
}
線程狀態(tài)以及優(yōu)先級
thread.getState()
thread.setPriority(int requestedPriority)
thread.getPriority();
守護(hù)線程
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程)
用個比較通俗的比如葵陵,任何一個守護(hù)線程都是整個JVM中所有非守護(hù)線程的保姆:
只要當(dāng)前JVM實(shí)例中尚存在任何一個非守護(hù)線程沒有結(jié)束液荸,守護(hù)線程就全部工作;只有當(dāng)最后一個非守護(hù)線程結(jié)束時脱篙,守護(hù)線程隨著JVM一同結(jié)束工作娇钱。
Daemon的作用是為其他線程的運(yùn)行提供便利服務(wù),守護(hù)線程最典型的應(yīng)用就是 GC (垃圾回收器)涡尘,它就是一個很稱職的守護(hù)者忍弛。
User和Daemon兩者幾乎沒有區(qū)別,唯一的不同之處就在于虛擬機(jī)的離開:如果 User Thread已經(jīng)全部退出運(yùn)行了考抄,只剩下Daemon Thread存在了细疚,虛擬機(jī)也就退出了。 因?yàn)闆]有了被守護(hù)者川梅,Daemon也就沒有工作可做了疯兼,也就沒有繼續(xù)運(yùn)行程序的必要了。
Thread daemonTread = new Thread();
// 設(shè)定 daemonThread 為 守護(hù)線程贫途,default false(非守護(hù)線程)
daemonThread.setDaemon(true);
// 驗(yàn)證當(dāng)前線程是否為守護(hù)線程吧彪,返回 true 則為守護(hù)線程
daemonThread.isDaemon();
并發(fā)
并發(fā):同一個對象被多個線程同時操作
synchronized
修飾方法
public class synTest {
public static void main(String[] args) {
DemoService service = new DemoService();
TestThread testThread = new TestThread(service);
new Thread(testThread,"線程1:").start();
new Thread(testThread,"線程2:").start();
}
}
class DemoService {
synchronized public void foo1() {
System.out.println(Thread.currentThread().getName() + "foo1開始了。丢早。姨裸。秧倾。");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "foo1結(jié)束了。傀缩。那先。。赡艰。");
}
synchronized public void foo2() {
System.out.println(Thread.currentThread().getName() + "foo2開始了售淡。。慷垮。揖闸。");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "foo2結(jié)束了。料身。汤纸。。芹血。");
}
}
class TestThread implements Runnable {
private DemoService demoService;
public TestThread(DemoService demoService) {
this.demoService = demoService;
}
@Override
public void run() {
demoService.foo1();
demoService.foo2();
}
}
case1:foo1和foo2加鎖時輸出結(jié)果:
線程1:foo1開始了蹲嚣。。祟牲。隙畜。
線程1:foo1結(jié)束了。说贝。议惰。。乡恕。
線程1:foo2開始了言询。。傲宜。运杭。
線程1:foo2結(jié)束了。函卒。辆憔。。报嵌。
線程2:foo1開始了虱咧。。锚国。腕巡。
線程2:foo1結(jié)束了。血筑。绘沉。煎楣。。
線程2:foo2開始了车伞。转质。。帖世。
線程2:foo2結(jié)束了。沸枯。日矫。。绑榴。
case2:foo1和foo2沒加鎖時輸出結(jié)果:
線程1:foo1開始了哪轿。。翔怎。窃诉。
線程2:foo1開始了。赤套。飘痛。。
線程2:foo1結(jié)束了容握。宣脉。。剔氏。塑猖。
線程2:foo2開始了。谈跛。羊苟。。
線程1:foo1結(jié)束了感憾。蜡励。。阻桅。巍虫。
線程1:foo2開始了。鳍刷。占遥。。
線程1:foo2結(jié)束了输瓜。瓦胎。芬萍。。搔啊。
線程2:foo2結(jié)束了柬祠。。负芋。漫蛔。。
case3:foo1加鎖旧蛾,foo2沒解鎖時:
線程1:foo1開始了莽龟。。锨天。毯盈。
線程1:foo1結(jié)束了。病袄。搂赋。。益缠。
線程1:foo2開始了脑奠。。幅慌。捺信。
線程2:foo1開始了。欠痴。迄靠。。
線程2:foo1結(jié)束了喇辽。掌挚。。菩咨。吠式。
線程2:foo2開始了。抽米。特占。。
線程1:foo2結(jié)束了云茸。是目。。标捺。懊纳。
線程2:foo2結(jié)束了揉抵。。嗤疯。冤今。。
總結(jié):synchronized修飾方法時茂缚,鎖的是調(diào)用此方法的對象戏罢,即為this。查看源碼可以明白脚囊,修飾方法時候默認(rèn)使用synchronized(this)龟糕。case1可以看出,無論線程1還是線程2凑术,執(zhí)行foo1還是foo2的時候由于對象被鎖,所以調(diào)用此對象的其他線程無法進(jìn)入所意。case2則特地亂套淮逊,兩個線程隨機(jī)執(zhí)行對象方法。case3扶踊,由于foo1加鎖泄鹏,所以foo1之前期間不會被有其他線程進(jìn)入。
同步代碼塊
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
new Thread(() -> {
synchronized (list) {
list.add(1);
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
Lock
class A{
// 使用Lock的子類ReentrantLock
private final ReentrantLock lock=new ReentrantLock();
public void m(){
lock.lock();
try {
// 代碼塊
}finally {
lock.unlock();
}
}
}
synchronized和Lock的區(qū)別
<img src="../myhexo/source/images/image-20210428105514117.png" alt="image-20210428105514117" style="zoom:80%;" />
線程池
public class Testpool {
public static void main(String[] args) {
//1. 創(chuàng)建服務(wù)秧耗,創(chuàng)建線程池
//newFixedThreadPool 參數(shù)為線程池大小
ExecutorService service= Executors.newFixedThreadPool(10);
// 執(zhí)行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2. 關(guān)閉連接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("hahahaha");
}
}