Java 线程,进程

进程
    1.运行时(runtime)应用程序
    2.进程之间的内存不是共享(独占)
    3.进程间通信使用socket(套接字)
    
多线程
    1.进程内并发执行的代码段
    2.线程之间共享内存
    3.每个运行着的线程对应一个stack
    4.应用线程至少有一个线程(主线程)
    
1.Thread.yield()方法
    让当前线程让出CPU的抢占权,瞬时的动作。
2.Thread.sleep()
    让当前线程休眠指定毫秒数
    释放CPU抢占权,和锁旗标没有关系。

3.Object: wait
    让当前线程进入锁旗标的等待队列。释放CPU抢占权,还释放锁旗标的监控权。
字符在内存中存储都是unicode编码。
要注意:将对象传递给线程时,传递的只是对象的引用,而不是对象的拷贝
public class TestCase1 {
    @Test
    public void test4() throws InterruptedException {
        Zhong zhong = new Zhong();
        
        Thread t1 = new MyThread2(zhong, "中秋快乐");
        Thread t2 = new MyThread2(zhong, "你就是我最美的期待");
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        System.out.println("所有线程结束");
        System.out.println(Thread.currentThread().getName() + ", zhong = " + zhong + ", desc = " + zhong.getDesc());
    }
}

class MyThread2 extends Thread{
    private Zhong zhong;
    
    public MyThread2(Zhong zhong, String desc) {
        this.zhong = zhong;
        this.zhong.setDesc(desc);
    }
    public void run() {
        System.out.println(Thread.currentThread().getName() + ", zhong = " + zhong + ", desc = " + zhong.getDesc());
    }
}

class Zhong{
    private String desc;

    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
}

打印结果:
Thread-0, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待
Thread-1, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待
所有线程结束
main, zhong = day16.Zhong@70212102, desc = 你就是我最美的期待

继承Thread并重写run方法来定义线程

 * 第一种创建线程的方式
 * 继承Thread并重写run方法来定义线程要执行的任务

public class Demo1 {
    public static void main(String[] args) {
        myThread1 t1 = new myThread1();
        myThread2 t2 = new myThread2();
        
         * 启动线程要指定start方法,而不是直接调用run方法,
         * run方法时线程要执行的任务。
         * 当线程的start方法被调用后, 线程进入runnable状态,
         * 一旦获取cpu时间,run方法会自动调用。
        t1.start();
        t2.start();
    }
}

 * 这种创建线程的方式有两个不足:
 * 1. 由于java是单继承,那么当继承了Thread后,就无法再继承其他类。
 * 
 * 2.由于继承Thread后重写run方法规定了线程执行的任务,
 * 这就导致线程与任务有一个必然的耦合关系,不利于线程的重用。

class myThread1 extends Thread{
    public void run() {
        for(int i=0; i<250; i++) {
            System.out.print("*");
            if(i>0 && i%50==0) {
                System.out.println();
            }
        }
    }
}
class myThread2 extends Thread{
    public void run(){
        for(int i=0; i<250; i++) {
            System.out.print("-");
            if(i%50==0) {
                System.out.println();
            }
        }
    }
}

实现Runnable接口并重写run方法,实例传入Thread中

 * 第二种创建线程的方式
 * 实现Runnable接口并重写run方法

public class Demo2 {
    public static void main(String[] args) {
        MyRunnable1 r1 = new MyRunnable1();
        MyRunnable2 r2 = new MyRunnable2();
        
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        
        t1.start();
        t2.start();
    }
}

class MyRunnable1 implements Runnable{
    public void run() {
        for(int i=0; i<250; i++) {
            System.out.print("*");
            if(i%50==0) {
                System.out.println();
            }
        }
    }
}

class MyRunnable2 implements Runnable{
    public void run() {
        for(int i=0; i<250; i++) {
            System.out.print("-");
            if(i%50==0) {
                System.out.println();
            }
        }
    }
}

匿名内部类创建上面两种线程

 * 使用匿名内部类来完成方式一与方式二的线程创建

public class Demo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run() {
                for(int i=1; i<251; i++) {
                    System.out.print("*");
                    if(i%50 == 0) {
                        System.out.println();
                    }
                }
            }
        };
        
        t1.start();

        -------------方式二
        new Thread(new Runnable() {
            public void run() {
                for(int i=1; i<250; i++) {
                    System.out.print("-");
                    if(i%50 == 0) {
                        System.out.println();
                    }
                }
            }
        }).start();
    }
}

获取运行当前方法的线程,Thread.currentThread()

 * static Thread currentThread()
 * 获取运行当前方法的线程

public class Demo4 {
    public static void main(String[] args) {
        Thread main = Thread.currentThread();
        System.out.println("运行main线程:" + main);
        dosome();
    }
    public static void dosome() {
        Thread t = Thread.currentThread();
        System.out.println("运行dosome线程:"+t);
        Thread t2 = new Thread() {
            public void run() {
                System.out.println("运行自建线程:"+Thread.currentThread());
            }
        };
        t2.start();
    }
}

打印:
运行main线程:Thread[main,5,main]
运行dosome线程:Thread[main,5,main]
运行自建线程:Thread[Thread-0,5,main]

获取线程相关信息的方法

 * 获取线程相关信息的方法
public class Demo5 {
    public static void main(String[] args) {
        Thread main = Thread.currentThread();
        String mainName = main.getName();
        long mainId = main.getId();
        int priority = main.getPriority();
        
        System.out.println("name:"+mainName);
        System.out.println("id:"+mainId);
        System.out.println("优先级:"+priority);
        
        boolean isAlive = main.isAlive();
        System.out.println("是否存活"+isAlive);
        
        boolean isDaemon = main.isDaemon();
        System.out.println("是否是守护线程:"+isDaemon);
        
        boolean isInterrupted = main.isInterrupted();
        System.out.println("是否中断:"+isInterrupted);
        
    }
}

打印:
name:main
id:1
优先级:5
是否存活true
是否是守护线程:false
是否中断:false


 * 线程优先级
 * 线程的时间片分配完全听线程调度的,线程只能被动的被分配时间,对于线程调度的工作不能干预。
 * 
 * 但是可以通过提高线程的优先级来达到尽可能干预的目的。
 * 理论上,优先级越高的线程,获取CPU的时间片的次数就越多。

t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);

sleep,线程堵塞

import java.text.SimpleDateFormat;
import java.util.Date;

 * static void sleep(long ms)
 * 线程提供的静态方法sleep可以使运行该方法的线程进入堵塞
 * 状态指定毫秒,超时后线程会自动回到RUNNABLE状态。

public class Demo7 {
    public static void main(String[] args) {
        while(true) {
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            String time = sdf.format(new Date());
            System.out.println(time);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

打印:  一秒钟打印一次当前时间
08:25:16
08:25:17
08:25:18

yield方法让出CPU时间

 * yield方法
 * Thread的静态方法yield:static void yield()
 * 该方法用于使当前线程主动让出当次CPU时间片段回到RUNNABLE状态,等待分配时间片。

package archive;

public class cc {
    public static void main(String[] args) {
        Thread t1 = new MyThread("Thread-1");
        Thread t2 = new MyThread("Thread-2");
        
        t1.start();
        t2.start();
    }
}

class MyThread extends Thread{
    private String name;

    public MyThread(String name) {
        this.name = name;
    }
    
    public void run() {
        for(;;) {
            System.out.println(name);
            Thread.yield();
        }
    }
}

守护线程,所有前台线程结束,守护线程就会被结束

 * 守护线程,又称为后台线程
 * 当一个进程中的所有前台线程都结束时,进程就要结束,
 * 若还有后台线程运行,那么后台线程会被强制结束。

public class Demo8 {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                for(int i=0; i<10; i++) {
                    System.out.println("hello");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        
        Thread t2 = new Thread() {
            public void run() {
                while(true) {
                    System.out.println("hi");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        
        -------- 设置为后台线程,并且要在start前调用
        t2.setDaemon(true);
        
        t1.start();
        t2.start();
    }
}

运行结果:
t2 跟随 t1 结束而结束

 * main方法是一个前台线程,GC是一个后台线程

join 堵塞线程,等待某个线程执行完毕,再继续执行

 * void join()
 * join方法可以使调用该方法的线程进入堵塞状态,直到该方法所属
 * 线程完成工作才会解除调用该方法线程的堵塞状态。
 * join方法一般用来完成多个线程之间的同步工作问题。

public class Demo9 {
    static boolean isFinish = false;
    public static void main(String[] args) {
        Thread download = new Thread() {
            public void run() {
                for(int i=1; i<=6; i++) {
                    System.out.println("download:"+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("download is successful");
                isFinish = true;
            }
        };
        
        Thread show = new Thread() {
            public void run() {
                System.out.println("showing");
                -- 要先等待图片下载完毕
                try {
                    download.join();    ---- 等待download线程执行完毕
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(!isFinish) {
                    throw new RuntimeException("show:图片显示异常");
                }
                System.out.println("show end");
            }
        };
        
        download.start();
        show.start();
    }
}

打印:
showing
download:1%
download:2%
download:3%
download:4%
download:5%
download:6%
download is successful
show end

一个很明显join堵塞的例子
package archive;

public class cc {
    public static void main(String[] args) {
        Thread t1 = new MyThread("Thread-4", 4000);
        Thread t2 = new MyThread("Thread-3", 3000);
        Thread t3 = new MyThread("Thread-2", 2000);
        Thread t4 = new MyThread("Thread-1", 1000);
        
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        
        try {
            t1.join();
            System.out.println("-----------");
            t2.join();
            System.out.println("-----------");
            t3.join();
            System.out.println("-----------");
            t4.join();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread{
    private String name;
    private int time;
    
    public MyThread(String name, int time) {
        this.name = name;
        this.time = time;
    }
    
    public void run() {
            System.out.println(name + "等待");
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "到了");
    }
}
  在t1.join()堵塞直到t1线程结束后才走下一句
执行结果:
Thread-3等待
Thread-1等待
Thread-2等待
Thread-4等待
Thread-1到了
Thread-2到了
Thread-3到了
Thread-4到了
-----------
-----------
-----------

synchronized,同步锁,线程排队执行

在方法上添加,是给对象加锁,如果是静态方法,静态方法所属类,就是给类加锁

 * 多线程并发访问同一资源时,就会形成“抢”的现象,
 * 由于线程切换时机不确定,可能导致执行代码顺序的混乱,严重时会导致系统瘫痪。
 * 
 * 当一个方法被synchronized修饰后,该方法为同步方法,
 * 即:多个线程不能同时进入方法内部执行。
 * 对于成员方法而言,synchronized会在一个线程调用该方法将该方法所属对象加锁,

 * 其他线程在执行该方法时由于执行方法的线程没有释放锁,所以只能在方法外堵塞,
 * 直到持有方法锁的线程将方法执行完毕。
 * 所以,解决多线程并发执行安全问题的办法就是将"抢"变为"排队"

public class Demo1 {
    public static void main(String[] args) {
        Bean bean = new Bean();
        Thread t1 = new Thread() {
            public void run() {
                while(true) {
                    bean.OperateB();    
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                while(true) {
                    bean.OperateB();        
                }
            }
        };
        t1.start();
        t2.start();
    }   
}

class Bean{
    static int b = 20;
    public synchronized void OperateB() {
        b--;
        if(b<=0) {
            throw new RuntimeException("b is end!");
        }else {
            System.out.println(Thread.currentThread()+" b="+b);
        }
    }
}

同步块,块中的代码同一时间只能被一个线程执行

 * 同步块
 * 有效的缩小同步范围可以在保证并发安全的同时尽可能提高并发效率。
 * 
 * 同步块可以要求多个线程对该块内的代码排队执行,但是前提条件是
 * 同步监视器对象即(上锁的对象)要求多个线程看到的必须是同一个。
 * 
 * synchronized(同步监视器对象){
 *      需要同步的代码
 * }
 * 
 * 所谓同步执行即:多个线程必须排队执行
 * 所谓异步执行即:多个线程可以同时执行

public class Demo2 {
    public static void main(String[] args) {
        Shop shop = new Shop();
        Thread t1 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        t1.start();
        t2.start();
    }
}

class Shop{
    public void buy() {
        System.out.println("shop is begin");
        try {
            Thread.sleep(2000);
            synchronized (this) {
                System.out.println(Thread.currentThread() + "--hello");
                Thread.sleep(2000);
            }
        }catch(Exception e) {
            
        }
        System.out.println("shop is end");
    }
}
注意:
synchronized(this) 
当中 this 指代的是 synchronized 所在的【对象】,

为 this 时本程序的执行的结果是:
shop is begin
shop is begin
Thread[Thread-1,5,main]--hello   --- 锁住了
shop is end
Thread[Thread-0,5,main]--hello
shop is end

当 this 改为 new Object 时,
shop is begin
shop is begin
Thread[Thread-0,5,main]--hello   --- 锁无效了
Thread[Thread-1,5,main]--hello
shop is end
shop is end

同步监视器对象即(上锁的对象)要求多个线程看到的必须是同一个。

类的静态方法加上 sysnchronized,那么类实例出来的对象执行该方法时也是具有同步锁的效果,因为该方法全局就一份,与对象无关

 * 静态方法的同步锁
 * 当一个静态方法被synchronized修饰后,那么该方法即为同步方法,
 * 由于静态方法从属类,全局就一份,所以同步的静态方法一定具有同步锁效果,与对象无关。

public class Demo3 {
    public static void main(String[] args) {
        Foo f1 = new Foo();
        Foo f2 = new Foo();
        Thread t1 = new Thread() {
            public void run() {
                f1.dosome();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                f2.dosome();
            }
        };
        t1.start();
        t2.start();
    }
}

class Foo{
    public synchronized static void dosome() {
        try {
            System.out.println("dosome");
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+": dosome is running");
            Thread.sleep(3000);
            System.out.println(t.getName()+": dosome is end");
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

打印:
dosome
Thread-0: dosome is running
Thread-0: dosome is end
dosome
Thread-1: dosome is running
Thread-1: dosome is end

互斥锁,,要十分注意锁的对象在多线程中看到的是否是同一个

 * 互斥锁
 * synchronized也叫互斥锁,即:
 * 使用synchronized修饰多段代码,只要他们的同步监视器对象相同,
 * 那么这几段代码间就是互斥关系,即:多个线程不能同时执行这些代码

public class Demo4 {
    public static void main(String[] args) {
        Foo1 foo = new Foo1();
        Thread t1 = new Thread() {
            public void run() {
                foo.methA(); 
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                foo.methB();
            }
        };
        t1.start();
        t2.start();
    }
}

class Foo1{
    public synchronized void methA() {
        try {
            System.out.println("methA is begin");
            Thread.sleep(4000);
            System.out.println("methA is end");
        }catch(Exception e) {
            
        }
    }
    public synchronized void methB() {
        try {
            System.out.println("methB is begin");
            Thread.sleep(4000);
            System.out.println("methB is end");
        }catch(Exception e) {
            
        }
    }
}

打印:
methA is begin        ----- 尽管运行的不是同一个方法,但是锁的是同一个对象
methA is end
methB is begin
methB is end

** 注意:互斥锁修饰多段代码,同步锁修饰一段代码

ArrayList,HashSet ,HashMap 都不支持线程安全,要使用Collections内置的静态方法转换为线程安全的数据模型。

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Demo5 {
    public static void main(String[] args) {

         * ArrayList 不是线程安全的
        List<String> list = new ArrayList<String>();
        list.add("one");
        list.add("two");
        list.add("three");
        System.out.println(list);

         * 将给定集合转换为线程安全的集合
        list = Collections.synchronizedList(list);
        System.out.println(list);
        

         * HashSet 不是线程安全的
        Set<String> set = new HashSet<String>(list);
        set.add("a");
        set.add("b");
        System.out.println(set);
        
         * 将给定的 Set 集合转换为线程安全的
        set = Collections.synchronizedSet(set);
        System.out.println(set);
        

         * HashMap 也不是线程安全的
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("Language",98);
        map.put("Math",90);
        map.put("Englist",80);
        System.out.println(map);
        
         * 将 Map 转换为线程安全的
        map = Collections.synchronizedMap(map);
        System.out.println(map);
    }
}


 * API手册上有说明
 * 就算是线程安全的集合那么其中对于元素的操作,如:add,remove等方法
 * 都不予迭代器遍历做互斥,需要自行维护互斥关系。

线程池,重用线程,控制线程数量

 * 线程池
 * 线程池主要有两个作用:
 * 1. 重用线程   ----- 而不是销毁后重建线程
 * 2. 控制线程数量
 * 当我们的应用需要创建大量线程或者发现线程会频繁的创建和销毁时
 * 就应当考虑使用线程池来维护线程。


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo6 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        for(int i=0; i<3; i++) {
            Runnable runn = new Runnable() {
                public void run() {
                    Thread t = Thread.currentThread();
                    try {
                        System.out.println(t.getName()+"线程开始");
                        Thread.sleep(3000);
                        System.out.println(t.getName()+"线程结束");
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            threadPool.execute(runn);
            System.out.println("指派了一个任务");
        }
        System.out.println("设置结束");

         * 停止线程池有两种方式:
         * 1. threadPool.shutdown();    ---- 分配的任务执行完毕就销毁所有线程
         * 2. threadPool.shutdownNow(); ---- 立即结束所有线程

        threadPool.shutdown();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容