多线程 Thread 线程同步 synchronized

1.多线程基础以及两种启用方式

/**
 * 多线程
 * 多线程改变了代码的执行方式,从原有的所有代码都串行操作改变为多个代码片段之间并行操作。
 * 因此多线程允许多个代码片段"同时运行"。
 *
 * 创建线程的方式有两种
 * 1:继承线程并重写run方法,在run方法中定义线程要执行的任务。
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread1();
        Thread t2 = new MyThread2();
        /*
         * 启动线程是调用线程的start方法而不是直接调用run方法。
         * 当线程的start方法调用后,线程纳入到线程调度中,当其第一次分配到时间片开
         * 始运行时,它的run方法会自动被执行。
         */
        t1.start();
        t2.start();
    }
}
/**
 * 第一种创建线程的方式优点是创建简单方便
 * 但是缺点也比较明显:
 * 1:由于java是单继承的,这导致继承了线程就无法再继承其他的类,这会导致无法重用其他
 *   超类的方法而产生继承冲突问题。
 * 2:定义线程的同时重写run方法,这就等于规定了线程要执行的具体任务,导致线程与其执行的
 *   任务产生必然的耦合关系,不利于线程的重用
 */
class MyThread1 extends Thread{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("你愁啥?");
        }
    }
}
class MyThread2 extends Thread{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("瞅你咋地!");
        }
    }
} 
/**
 * 第二种创建线程的方式
 * 实现Runnable接口并重写run方法来单独定义线程的任务。
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        //单独实例化任务
        Runnable r1 = new MyRunnable1();
        Runnable 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<1000;i++) {
            System.out.println("你愁啥?");
        }
    }
}
class MyRunnable2 implements Runnable{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("瞅你咋地!");
        }
    }
}

2.使用匿名内部类方式创建多线程

/**
 * 使用匿名内部类的创建形式完成线程的两种创建
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //第一种创建方式
        Thread t1 = new Thread() {
            public void run() {
                for(int i=0;i<1000;i++) {
                    System.out.println("你是谁啊?");
                }
            }
        };
        //第二种创建方式
        Runnable r2 = new Runnable() {
            public void run() {
                for(int i=0;i<1000;i++) {
                    System.out.println("我是查水表的!");
                }
            }
        };
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
    }
}

3.获取线程信息的相关方法

/**
 * 获取线程信息的相关方法
 */
public class ThreadInfoDemo {
    public static void main(String[] args) {
        //获取主线程
        Thread main = Thread.currentThread();
        //获取线程的名字
        String name = main.getName();
        System.out.println("name:"+name);
        //获取唯一标识
        long id = main.getId();
        System.out.println("id:"+id);
        //获取线程的优先级
        int priority = main.getPriority();
        System.out.println("优先级:"+priority);
        boolean isAlive =  main.isAlive();
        boolean isDaemon = main.isDaemon();
        boolean inInterrupted = main.isInterrupted();
        System.out.println("isAlive:"+isAlive);
        System.out.println("isDaemon:"+isDaemon);
        System.out.println("inInterrupted:"+inInterrupted);
    }
}
/**
 * 线程提供了一个静态方法:
 * static Thread currentThread()
 * 该方法可以获取运行这个方法的线程
 *
 * 实际上java的所有代码都是靠线程运行的,main方法也不例外。运行main方法的线程不是由我们
 * 创建的,而是JVM自行创建的,并用来运行main方法。而我们通常称这个线程为"主线程"
 */
public class CurrentThreadDemo {
    public static void main(String[] args) {
        Thread main = Thread.currentThread();
        System.out.println("运行main方法的线程是:"+main);
        dosome();
        Thread t = new Thread() {
            public void run() {
                Thread t = Thread.currentThread();
                System.out.println("自定义线程:"+t);
                dosome();
            }
        };
        t.start();
    }

    public static void dosome() {
        Thread t = Thread.currentThread();
        System.out.println("运行dosome方法的线程是:"+t);
    }
}

4.线程优先级

/**
 * 线程的优先级
 * 线程启动后就纳入到了线程调度中统一管理,什么时候获取CPU时间片完全取决于线程调度,
 * 线程是不能主动索取的,通过调整线程的优先级可以最大程度的干涉分配CPU时间片的几率。
 * 理论上线程优先级越高的线程获取CPU时间片的几率越高。
 *
 * 线程的优先级有10个等级,用整数1-10表示,1是最小,5是默认,10是最高
 */
public class PriorityDemo {
    public static void main(String[] args) {
        Thread min = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("min");
                }
            }
        };
        Thread norm = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("nor");
                }
            }
        };
        Thread max = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("max");
                }
            }
        };
        min.setPriority(Thread.MIN_PRIORITY);
        max.setPriority(Thread.MAX_PRIORITY);
        min.start();
        norm.start();
        max.start();
    }
}

5.sleep方法

/**
 * 线程提供了一个静态方法:
 * static void sleep(long ms)
 * 该方法可以让运行这个方法的线程处于阻塞状态指定的毫秒,超时后线程会自动回到RUNNABLE
 * 状态再次并发运行。
 */
public class SleepDemo {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("程序结束了");
    }
}
/**
 * sleep方法要求处理中断异常
 * 当一个线程调用sleep方法处于阻塞状态的过程中,此时被其他线程调用了该线程的interrupt
 * 方法,那么就会打断这个线程的睡眠阻塞,此时sleep方法就会抛出中断异常告知。
 */
public class SleepDemo2 {
    public static void main(String[] args) {
        Thread lin = new Thread() {
            public void run() {
                System.out.println("林:刚美完容,睡一会...");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
                }
                System.out.println("林:醒了!");
            }
        };
        Thread huang = new Thread() {
            public void run() {
                System.out.println("黄:开始砸墙!");
                for(int i=0;i<5;i++) {
                    System.out.println("黄:80!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("咣当!");
                System.out.println("黄:搞定!");
                //中断lin的阻塞状态
                lin.interrupt();
            }
        };
        lin.start();
        huang.start();
    }
}

6.线程之间的同步方法join

/**
 * join方法允许当前线程在join方法所属线程上等待,直到该线程结束后结束join阻塞继续后续
 * 操作。所以join方法可以协调线程的同步运行。
 *
 * 同步运行:多个线程之间执行有顺序。
 * 异步运行:多个线程之间各自执行各自的。
 */
public class JoinDemo {
    private static boolean isFinish = false;
    public static void main(String[] args) {
        /*
         * 当一个方法的局部内部类中引用了这个方法的其他局部变量时,该变量必须声明为final的。
         * JDK8之后可以不写final.但是该变量依然会被编译器最终改为final的。
         * 这源自JVM的内存分配问题。
         */
//        boolean isFinish = false;
        Thread download = new Thread() {
            public void run() {
                System.out.println("down:开始下载图片!");
                for(int i=1;i<=100;i++) {
                    System.out.println("down:"+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("down:图片下载完毕!");
                isFinish = true;
            }
        };
        Thread show = new Thread() {
            public void run() {
                try {
                    System.out.println("show:开始显示文字...");
                    Thread.sleep(3000);
                    System.out.println("show:文字显示完毕!");
                    System.out.println("show:开始显示图片...");
                    /*
                     * 先等待download线程执行完毕(图片下载完)之后在继续后续工作
                     */
                    download.join();
                    if(!isFinish) {
                        throw new RuntimeException("图片加载失败!");
                    }
                    System.out.println("show:显示图片完毕!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        download.start();
        show.start();
    }
}

7.守护线程(后台线程)daemon

/**
 * 守护线程
 * 守护线程也称为后台线程,创建和使用上与前台线程一样,但是有一点不同:
 * 当进程结束时,所有正在运行的守护线程都会被强制停止。
 *
 * 进程的结束:当所有普通线程都结束时,进程结束
 */
public class DaemonThreadDemo {
    public static void main(String[] args) {
        Thread rose = new Thread() {
            public void run() {
                for(int i=0;i<5;i++) {
                    System.out.println("rose:let me go!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("rose:啊啊啊啊啊啊AAAAAAaaaaa....");
                System.out.println("噗通!");
            }
        };

        Thread jack = new Thread() {
            public void run() {
                while(true) {
                    System.out.println("jack:you jump!i jump!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        rose.start();
        /*
         * 设置为守护线程要在线程启动前进行
         */
        jack.setDaemon(true);
        jack.start();
    }
}

8.线程同步

/**
 * 多线程并发的安全问题
 * 当多个线程并发访问同一临界资源,由于线程切换时机不确定,导致多个线程操作该资源未
 * 按照程序设计的顺序进行,导致出现错误,严重时可能出现系统瘫痪等情况。
 *
 * 临界资源:同一时间只能被一条线程操作的资源
 */
public class SyncDemo {
    public static void main(String[] args) {
        Table table = new Table();
        Thread t1 = new Thread() {
            public void run() {
                while(true) {
                    int bean = table.getBean();
                    Thread.yield();//模拟CPU没有时间了
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                while(true) {
                    int bean = table.getBean();
                    Thread.yield();//模拟CPU没有时间了
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        t1.start();
        t2.start();
    }
}

class Table{
    //桌子上有20个豆子
    private int beans = 20;
    /**
     * 当一个方法使用关键字synchronized修饰后这个方法称为"同步方法",多个线程不能同时
     * 在方法内部执行。
     * 将多个线程异步操作临界资源改为同步操作就可以解决多线程的并发安全问题了。
     */
    public synchronized int getBean() {
        if(beans==0) {
            throw new RuntimeException("没有豆子了!");
        }
        /*
         * static void yield()
         * 当一个线程执行到这个方法时会主动让出本次CPU时间片并回到RUNNABLE状态。
         */
        Thread.yield();//模拟CPU没有时间了
        return beans--;
    }
}
/**
 * 同步块
 * 有效的缩小同步范围可以在保证并发安全的前提下尽可能的提高并发的效率
 *
 * synchronized(同步监视器对象){
 *     需要多线程同步运行的代码片段
 * }
 * 同步块可以更准确的锁定需要同步运行的代码片段,从而有效控制同步范围。
 */
public class SyncDemo2 {
    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{
    /*
     * 若在方法上直接使用synchronized,那么同步监视器对象就是当前方法所属对象this
     */
//    public synchronized void buy() {
    public void buy() {
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在挑衣服...");
            Thread.sleep(5000);
            /*
             * 使用同步块时要注意,多个需要同步运行该代码片段的线程看到的同步监视器对象,即
             * 上锁的对象必须是同一个才可以。否则没有同步效果!

             * 下面注释的代码就是个错误的用法,当多个线程看到的同步监视器对象不是同一个时,
             * 是没有同步效果的。
             */
//            synchronized (new Object()) {
            synchronized (this) {
                System.out.println(t.getName()+":正在试衣服...");
                Thread.sleep(5000);
            }
            System.out.println(t.getName()+":结账离开!");
        } catch (Exception e) {
        }
    }
}
/**
 * 静态方法若使用synchronized修饰后,那么该方法一定具有同步效果。
 *
 * 静态方法的同步监视器对象使用的是当前类的类对象(Class的实例)
 * 类对象后面反射的课程中会介绍。
 */
public class SyncDemo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                Boo.dosome();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                Boo.dosome();
            }
        };
        t1.start();
        t2.start();
    }
}

class Boo{
//    public synchronized static void dosome() {
    public static void dosome() {
        synchronized (Boo.class) {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在执行dosome方法...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }
            System.out.println(t.getName()+":运行dosome方法完毕!");
        }
    }
}
/**
 * 互斥锁
 * 当使用synchronized锁定多个代码片段,并且指定的同步监视器对象是同一个时,这些代码片
 * 段间就是互斥的,多个线程不能同时在这些代码片段间一起执行。
 */
public class SyncDemo4 {
    public static void main(String[] args) {
        Foo foo = new Foo();
        Thread t1 = new Thread() {
            public void run() {
                foo.methodA();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                foo.methodB();
            }
        };
        t1.start();
        t2.start();
    }
}

class Foo{
    public synchronized void methodA() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName()+":正在执行A方法...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        System.out.println(t.getName()+":执行A方法完毕!");
    }
    public synchronized void methodB() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName()+":正在执行B方法...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        System.out.println(t.getName()+":执行B方法完毕!");
    }
}

9.倒计时demo

public class Test {
    public static void main(String[] args) {
        /*
         * 倒计时程序
         * 程序启动后,要求在控制台输入一个数然后从这个数开始每秒递减并输出该数
         * 到0时输出时间到,程序结束。
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个数字:");
        int num = Integer.parseInt(scanner.nextLine());
        for(;num>0;num--) {
            System.out.println(num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("时间到!");
    }
}

原文地址:https://www.cnblogs.com/hello4world/p/12151039.html

时间: 2024-11-01 14:50:22

多线程 Thread 线程同步 synchronized的相关文章

java多线程之 ---- 线程同步

java多线程之线程同步 线程同步 定义:同步是指在同一时间段内只能运行一个线程. 分类:同步方法.同步块. 作用:安全解决共享问题. 同步块: 语法: synchronized (同步对象) { 需要同步的代码; } 例子: public class ThreadDemo implements Runnable{ private int ticket = 5; public void run(){ for(int i=1;i<=5;i++){ synchronized (this){ if(t

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例

MFC——9.多线程与线程同步

Lesson9:多线程与线程同步 程序.进程和线程是操作系统的重点,在计算机编程中,多线程技术是提高程序性能的重要手段.本文主要讲解操作系统中程序.进程和线程之间的关系,并通过互斥对象和事件对象实例说明多线程和线程同步技术. 1.      程序.进程和线程 1.1  程序和进程 程序是计算机指令的集合,它以文件的形式存储在磁盘上.进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动.进程是资源申请.调度和独立运行的单位,因此,它使用系统中的运行资源:而程序不能

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

C#多线程之线程同步3

在上一篇C#多线程之线程同步2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier构造.ReaderWriterLockSlim构造和SpinWait构造. 七.使用Barrier构造 在这一小节中,我们将学习一个比较有意思的同步构造:Barrier.Barrier构造可以帮助我们控制多个等待线程达到指定数量后,才发送通知信号,然后所有等待线程才能继续执行,并且在每次等待线程达到指

mfc小工具开发之定时闹钟之---多线程急线程同步

一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等.用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等.但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务. 在MFC中,一般用全局函数Afx

多线程之线程同步

多线程内容大致分两部分,其一是异步操作,可通过专用,线程池,Task,Parallel,PLINQ等,而这里又涉及工作线程与IO线程:其二是线程同步问题,鄙人现在学习与探究的是线程同步问题. 通过学习<CLR via C#>里面的内容,对线程同步形成了脉络较清晰的体系结构,在多线程中实现线程同步的是线程同步构造,这个构造分两大类,一个是基元构造,一个是混合构造.所谓基元则是在代码中使用最简单的构造.基元构造又分成两类,一个是用户模式,另一个是内核模式.而混合构造则是在内部会使用基元构造的用户模

Java线程(二):线程同步synchronized和volatile

上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程共享的,一个线程改变了其成员变量num值,下一个线程正巧读到了修改后的num,所以会递增输出. 要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性.多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现.拿上篇博文中的例子来说明,在多个线程之间共享了Count类的

C# 多线程之线程同步

多线程间应尽量避免同步问题,最好不要线程间共享数据.如果必须要共享数据,就需要使用同步技术,确保一次只有一个线程访问和改变共享状态. 一::lock语句 lock语句事设置锁定和接触锁定的一种简单方法.其语法非常简单: lock (obj) { // 需要发生同步的代码区 } 将共享数据的操作代码,放在上述的"{...}"区域内.锁定的对象(obj)必须是引用类型,如果锁定一个值类型,实际是锁定了它的一个副本,并没有实现锁定功能. 一般地,被锁定对象需要被创建为 私有 只读 引用类型: