目录
线程实现方式
多线程实现的方式一
线程实现方式二:Runnable接口
线程实现三:实现Callable接口定义线程
主线程不等待问题
synchronized
synchronized 修饰代码块
synchronized 修饰成员方法
synchronized 修饰静态成员方法
死锁案例
synchronized为可重入锁
多线程特性
可见性
有序性
原子性
锁
Lock锁
读写锁
线程实现方式
多线程实现的方式一
[1]继承Thread类实现线程 重写run()方法
[2]使用start()开启子线程
[3]我们调用start-->底层调用start0() 底层不是Java书写-->run();
public class MyThread {
public static void main(String[] args){
//兔子
ThreadTest threadTest = new ThreadTest();
threadTest.setName("兔子"); //设置线程的名字
threadTest.start();
//乌龟
ThreadTest threadTest1 = new ThreadTest();
threadTest1.setName("乌龟");
threadTest1.start();
}
}
class ThreadTest extends Thread{
public void run(){
for (int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
}
//也可以使用匿名内部类的方法,书写线程
public static void main(String[] args) {
//匿名内部类
Thread thread1 = new Thread() {
public void run() {
//
}
};
thread1.start();
}
线程实现方式二:Runnable接口
优点:一个类如果已经继承另外的类 现在还可以实现线程操作
【1】实现Runnable接口 重写run()方法
【2】直接调用start()不可以开启,需要使用Thread有参数构造进行开启线程
【3】Thread--->implements Runable
public class Test3 {
public static void main(String[] args) {
MyRunnable my1 = new MyRunnable();
// MyRunnable my2 = new MyRunnable();
Thread th1 = new Thread(my1,"兔子");
Thread th2 = new Thread(my1,"乌龟");
th1.start();//开启线程
th2.start();//开启线程
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
}
public static void main(String[] args) {
//使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
//定义代码
}
}, "线程A").start();
//使用Lambda表达式 ()->{} 相当与重写了Runnnable中run()方法
new Thread(()->{
//定义代码
},"线程B").start();
}
线程实现三:实现Callable接口定义线程
最大的特点 就是可以接受线程执行方法结束的返回值
start()--->start0() -->run()
public class Test5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable my1 = new MyCallable();
//Callable类型-->FutureTask类型implements RunnableFuture extends Runnable
FutureTask futureTask = new FutureTask<>(my1);
Thread thread = new Thread(futureTask);
thread.start();//开启线程
//获得当前线程执行解说返回值
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyCallable implements Callable{
@Override
public Integer call() throws Exception {
int i = new Random().nextInt(10);
System.out.println(Thread.currentThread().getName()+"---"+i);
return null;
}
}
主线程不等待问题
方式一:主线程sleep
方式二:子线程join
方式三:使用CountDownLatch
凡是四:使用CycleBarrier
public class Test6 {
//private static CountDownLatch latch = new CountDownLatch(10);
//方式三 初始设置和子线程个数相同的计数器,子线程执行完毕后计数器减一,直到全部子线程执行完毕
//private static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
//方式四 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,
//屏障才会开门,所有被屏障拦截的线程才会继续干活。
public static void main(String[] args) {
Thread th = new Thread(new MyRun(),"线程A");
Thread th2 = new Thread(new MyRun(),"线程B");
th.start(); //开启线程A
th2.start(); //开启线程B
try {
Thread.sleep(200); //方式一
} catch (InterruptedException e) {
e.printStackTrace();
}
//th.join(); 方式二
System.out.println("我是输出语句");
}
}
class MyRun implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行输出");
}
}
没有try-catch中sleep执行结果如下图
由sleep执行结果
你会发现没有sleep代码的执行结果与你预想的结果不一样。因为主线程中开启了一个子线程,开启之后,主线程与子线程互不影响各自的生命周期,即主线程结束,子线程还可以继续执行;子线程结束,主线程也能继续执行。这样就解决了你疑惑的其中之一,为什么我都执行完了线程还可以输出结果。 疑惑其二,为啥主线程先输出? 因为主线程不等待子线程执行,子线程开启可以和运行需要时间。所以主线程可能会先输入。
synchronized
synchronized 修饰代码块
锁是一个Object类型的 但是注意:需要保证多个线程中使用是同一把锁才能锁住代码
A.固定值----“锁”
B.this称为对象锁 注意:操作线程传递的对象必须保证是同一个
C.Object.class 存放是一个class类型 称为类锁
public class MyTest {
public static void main(String[] args) {
Thread t = new Thread(new MyTicket(),"一号窗口");
Thread t1 = new Thread(new MyTicket(),"二号窗口");
t.start();
t1.start();
}
}
class MyTicket implements Runnable{
//代表的卖出的票数
static int num=1;
//要保证同一把锁,所以要加入static
static String str = new String();
@Override
public void run() {
while (num<=100){
synchronized (str){
if (num<=100){
System.out.println(Thread.currentThread().getName()+"卖出了第:"+num+"张票");
num++;
}else {
System.out.println("票已经售完");
}
}
}
}
}
无synchronized 你会执行结果你会发现如果你这么买票,你就可以下岗了。结果如下图:
有synchronized执行结果
原因synchronized就像相当于门锁一样,当你一个线程完成以后,我其他的线程才可以使用,继续竞争。
synchronized 修饰成员方法
【1】synchronized 不建议结合run()修饰使用
【2】synchronized 锁就是this
【3】因为当前锁是对象锁(this)所以必须保证使用的对象是同一个对象 如下图
synchronized 修饰静态成员方法
【1】当前就是类锁 传递对象的字节码 。class
【2】同一个类创建出不同对象 他们对应的。getClass()是一样的 如下图
public class MyTest2 {
public static void main(String[] args) {
MyTickett myTickett =new MyTickett();
for(int i=1;i<=5;i++){
Thread t = new Thread(myTickett,i+"");
t.start();
}
}
}
class MyTickett implements Runnable{
//代表的卖出的票数
static int num=1;
//要保证同一把锁,所以要加入static
static String str = new String();
@Override
public void run() {
while (num<=1000){
demo01();
}
}
//修饰静态方法 类锁 .class
public synchronized static void demo02(){
if (num<=100){
System.out.println(Thread.currentThread().getName()+"卖出了第:"+num+"张票");
num++;
}else {
System.out.println("票已经售完");
}
}
//修饰普通成员方法---对象锁(this)
public synchronized void demo01(){
if (num<=1000){
System.out.println(Thread.currentThread().getName()+"卖出了第:"+num+"张票");
num++;
}else {
System.out.println("票已经售完");
}
}
}
死锁案例
什么是死锁:1.多个线程共享多个资源 2.多个线程都需要其他线程的资源,每个线程又不愿意或者无法放弃自己的资源
synchronized 没有释放锁方法,所以处理的时候只可以尽量的避免
public class Test3 {
public static void main(String[] args) {
Thread th = new Thread(new xiaoming());
Thread th2 = new Thread(new xiaohong());
th.start();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
th2.start();
}
}
class xiaoming implements Runnable{
@Override
public void run() {
synchronized ("遥控器壳"){
System.out.println("小明抢到了遥控器壳");
synchronized ("电池"){
System.out.println("小明入手成功,可以看电视了");
}
}
}
}
class xiaohong implements Runnable{
@Override
public void run() {
synchronized ("电池"){
System.out.println("小红抢到了电池");
synchronized ("遥控器壳"){
System.out.println("小红入手成功,可以看电视了");
}
}
}
}
synchronized为可重入锁
public class Test4 {
public static void main(String[] args) {
//Lambda表达式
//synchronized为可重入锁
new Thread(()->{
synchronized ("锁"){
System.out.println("进出程序A");
synchronized ("锁"){
System.out.println("进入程序B");
}
}
}).start();
}
}
多线程特性
可见性
可见性:当多线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能立即看到修改的值。
可见性案例
若两个线程在不同的cpu,那么线程1改变了flag的值还没刷新到主存,线程2又使用了flag,那么这个flag值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。 使用volatile修饰全局变量达到可见性。
volatile修饰成员变量保持可见性 和有序性
public class TestA {
private static volatile boolean flag=true;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (flag){
}
}).start();
Thread.sleep(1000);
//主线程中的定义
flag = false;
}
}
有序性
有序性的本义是指在执行的时候,程序的代码执行顺序和语句的顺序是一致的。但是在Java内存模型中,是允许编译器和处理器对指令进行重排序的,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并执行的正确性。也就是说在多线程中代码的执行顺序,不一定会与你直观上看到的代码编写的逻辑顺序一致。
public class TestB {
public static void main(String[] args) {
int a=10;
int b=20;
int c=a+b;
}
}
原子性
原子性:是指一个操作或者一系列操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
public class TestC {
//CAS:保证了原子性
static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
//定义了10个线程
for (int j =0;j<10;j++){
//每个线程进行num++一万次
new Thread(() -> {
for (int i=0; i<10000;i++){
// num++;
atomicInteger.incrementAndGet();
}
}).start();
}
Thread.sleep(2000);
System.out.println(atomicInteger);
}
}
锁
Lock锁
1.多个线程使用的时候必须保证是同一个锁对象
2.使用lock()进行代码加锁 unlock()进行代码解锁
3.如果代码出现了异常 程序不会自动的解锁,所以使用lock的时候最好写到try catch finally代码中保证安全
public class TestA {
public static void main(String[] args) {
new Thread(new TicketRunable(),"窗口A").start();
new Thread(new TicketRunable(),"窗口B").start();
}
}
class TicketRunable implements Runnable{
static int num =1;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (num<=100){
lock.lock(); //给代码加锁操作
try {
if (num<=100){
System.out.println(Thread.currentThread().getName()+"卖出了第"+num+"张票");
num++;
}else {
System.out.println("票已经售完!!!!");
}
int a=10/0;
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
lock是可重入锁
public class TestB {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
lock.lock();
System.out.println("第一层");
lock.lock();
System.out.println("第二层");
lock.unlock();
lock.unlock();
}
}
优化lock案例
public class TestC {
public static void main(String[] args) {
//遥控器锁
ReentrantLock ykq= new ReentrantLock();
//电池锁
ReentrantLock dc= new ReentrantLock();
Thread th = new Thread(new xiaoming(ykq,dc));
Thread th2 = new Thread(new xiaohong(ykq,dc));
th.start();
th2.start();
}
}
class xiaoming implements Runnable{
//遥控器锁
private ReentrantLock ykq;
//电池锁
private ReentrantLock dc;
public xiaoming(ReentrantLock ykq, ReentrantLock dc) {
this.ykq = ykq;
this.dc = dc;
}
@Override
public void run() {
ykq.lock();
System.out.println("小明抢到了遥控器壳");
if (dc.isLocked()) {
//代表当前电池被锁了
ykq.unlock();
}
dc.lock();
System.out.println("小明入手成功,可以看电视了");
dc.unlock();
if (ykq.isLocked()){
ykq.unlock();
}
}
}
class xiaohong implements Runnable{
//遥控器锁
private ReentrantLock ykq;
//电池锁
private ReentrantLock dc;
public xiaohong(ReentrantLock ykq, ReentrantLock dc) {
this.ykq = ykq;
this.dc = dc;
}
@Override
public void run() {
dc.lock();
System.out.println("小红抢到了电池");
ykq.lock();
System.out.println("小红入手成功,可以看电视了");
ykq.unlock();
dc.unlock();
}
}
读写锁
public class TestA {
public static void main(String[] args) {
//获得读和写的锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//获取读锁
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
//获取写锁 这种类型的表示是内部类的表示形式
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println("--------------使用接口形式进行表示——————————————");
//使用多态表示
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//获得读锁
Lock readLock2= readWriteLock.readLock();
Lock readLock3= readWriteLock.readLock();
System.out.println(readLock2==readLock3); //true
//获得写锁
Lock writeLock2=readWriteLock.writeLock();
Lock writeLock3=readWriteLock.writeLock();
System.out.println(writeLock2 == writeLock3); //true
System.out.println(readLock2==writeLock2); //false;
}
}
使用readWriteLock实现读和读之间的共享,读和写、写和写之间的互斥
public class TestB {
public static void main(String[] args) {
new Thread(()->{
new Operator().read();
},"A").start();
new Thread(()->{
new Operator().read();
},"B").start();
new Thread(()->{
new Operator().write();
},"B").start();
new Thread(()->{
new Operator().write();
},"D").start();
}
}
class Operator{
// static Lock lock =new ReentrantLock();
//创建读写锁对象
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//这个方法就是一个读方法
public void read(){
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName()+"开始读操作");
//这个睡眠的操作 就是为了模拟真实读消耗的时间
try {
Thread.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束读操作");
readWriteLock.readLock().unlock();
}
//这个方法就是一个写方法
public void write(){
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"开始写操作");
//这个睡眠的操作 就是为了模拟真实读消耗的时间
try {
Thread.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束写操作");
readWriteLock.writeLock().unlock();
}
}
本人笔记



