多线程基础

  1. 第一种创建线程的方式:继承Thread并重写run方法来定义线程要执行的任务
public class ThreadDemo1 {

	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<1000;i++){
			System.out.println("你是谁呀?");
		}
	}
}
class MyThread2 extends Thread{
	public void run(){
		for(int i=0;i<1000;i++){
			System.out.println("我是查水表的");
		}
	}
}

  1. 第二种创建线程的方式:实现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("我是查水表的");
		}
	}
}

  1. 使用匿名内部类来完成方式一和方式二的线程创建
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("你是谁呀");
				}
			}
		};
		t1.start();
		
		//方式二
		Runnable runn=new Runnable() {
			public void run() {
				for(int i=0;i<1000;i++){
					System.out.println("我是查水表的");
				}
				
			}
		};
		Thread t2=new Thread(runn);
		t2.start();

	}

}

  1. static Thread currentThread()
    获取运行当前方法的线程
public class ThreadDemo4 {

	public static void main(String[] args) {
		Thread main=Thread.currentThread();//运行main方法的线程
		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);
	}

}
  1. 获取线程相关信息的方法
public class ThreadDemo5 {

	public static void main(String[] args) {
		// 获取运行main方法的线程
		Thread main=Thread.currentThread();
		long id=main.getId();
		System.out.println("id:"+id);//id:1   id值是系统分配的,不是我们能自己控制的
		
		String name=main.getName();
		System.out.println("name:"+name);//name:main
		
		int priority=main.getPriority();
		System.out.println("优先级:"+priority);//优先级:5
		
		boolean isAlive=main.isAlive();
		System.out.println("是否存活:"+isAlive);//是否存活:true
		
		boolean isDaemon=main.isDaemon();
		System.out.println("是否为守护线程:"+isDaemon);//是否为守护线程:false
		
		boolean isInterrupted=main.isInterrupted();
		System.out.println("是否被中断:"+isInterrupted);//是否被中断:false

	}

}

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

	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("norm");
				}
			}
		};
		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);
		//norm不设置,默认为5
		
		min.start();
		norm.start();
		max.start();

	}

}

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

	public static void main(String[] args) {
		/*
		 * 完成电子表功能
		 * 每秒输出一次当前系统时间:17:36:55
		 */
		SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
		while(true){
			System.out.println(sdf.format(new Date()));
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

}

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

	public static void main(String[] args) {
		/*
		 * rose:前台线程
		 */
		Thread rose=new Thread(){
			public void run(){
				for(int i=0;i<10;i++){
					System.out.println("rose:let me go");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						
					}
					
				}
				System.out.println("rose:aaaaa");
				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) {
						e.printStackTrace();
					}
				}
			}
		};
		//设置为后台线程--并且要在启动之前设置
		jack.setDaemon(true);
		rose.start();
		jack.start();

	}

}
  1. void join() join方法可以使调用该方法的线程进入阻塞状态,直到该方法所属线程完成工作才会解除调用该方法线程的阻塞状态。join方法一般用来完成多个线程之间的同步工作问题
public class ThreadDemo9 {
	//表示图片是否下载完毕
	public static boolean isFinish=false;
	
	public static void main(String[] args) {
		final 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(){
				System.out.println("show:开始显示图片...");
				/*
				 * 先等待download把图片下载完毕
				 */
				try {
					download.join();//调用download的join意思是:把show卡住,直到download把活干完
				} catch (InterruptedException e) {
					
				}
				
				if(!isFinish){
					throw new RuntimeException("图片没有下载完毕");
				}
				System.out.println("show:图片显示完毕");
			}
		};
		download.start();
		show.start();
	}

}

  1. 多线程并发访问同一资源时,就会形成“抢”的现象。由于线程切换时机不确定,可能导致执行代码顺序的混乱,严重时会导致系统瘫痪。
public class SyncDemo1 {

	public static void main(String[] args) {
		final Table table=new Table();
		Thread t1=new Thread(){
			public void run(){
				while(true){
					int bean=table.getBean();
					Thread.yield();
					System.out.println(getName()+":"+bean);
				}
			}
		};
		Thread t2=new Thread(){
			public void run(){
				while(true){
					int bean=table.getBean();
					Thread.yield();
					System.out.println(getName()+":"+bean);
				}
			}
		};
		t1.start();
		t2.start();

	}

}

class Table{
	//桌子上有20个豆子
	private int beans=20;
	
	 /**
	  * 当一个方法被synchronized修饰后,该方法为同步方法,即:多个线程不能同时进入方法内部执行
	  * 对于成员方法而言,synchronized会在一个线程调用该方法时将该方法所属对象加锁
	  * 其他线程在执行该方法时由于执行方法的线程没有释放锁,所以只能在方法外阻塞,直到持有方法锁
	  * 的线程将方法执行完毕。
	  * 所以,解决多线程并发执行安全问题的办法是将“抢”变为“排队”
	  * @return
	  */
	public synchronized int getBean(){
		if(beans==0){
			throw new RuntimeException("没有豆子了!");
		}
		Thread.yield();
		return beans--;
	}
}

  1. 同步块: 有效的缩小同步范围可以在保证并发安全的同时尽可能提高并发效率。
public class SyncDemo2 {

	public static void main(String[] args) {
		final 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(){
		//获取运行buy方法的线程
		Thread t=Thread.currentThread();
		try {
			System.out.println(t.getName()+":正在挑衣服");
			Thread.sleep(5000);
			/*
			 * 同步块可以要求多个线程对该块内的代码排队执行,但是前提条件是同步监视器对象
			 * 即(上锁的对象)要求多个线程看到的必须是同一个
			 * synchronized (同步监视器对象) {
					需要同步的代码
				}
			 * 所谓同步执行即:多个线程必须排队执行
			 * 所谓异步执行即:多个线程可以同时执行
			 */
			synchronized (t) {
				System.out.println(t.getName()+":正在试衣服");
				Thread.sleep(5000);
			}
			
			
			System.out.println(t.getName()+":结账离开");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

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

	public static void main(String[] args) {
		final Foo f1=new Foo();
		final 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 {
			Thread t=Thread.currentThread();
			System.out.println(t.getName()+":正在运行dosome方法");
			Thread.sleep(5000);
			System.out.println(t.getName()+":运行dosome方法完毕");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

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

	public static void main(String[] args) {
		final Boo boo=new Boo();
		Thread  t1=new Thread(){
			public void run(){
				boo.methodA();
			}
		};
		Thread  t2=new Thread(){
			public void run(){
				boo.methodB();
			}
		};
		t1.start();
		t2.start();

	}

}

class Boo{
	public synchronized void methodA(){
		try {
			Thread t=Thread.currentThread();
			System.out.println(t.getName()+":正在执行A方法");
			Thread.sleep(5000);
			System.out.println(t.getName()+":运行A方法完毕");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void  methodB(){
		try {
			Thread t=Thread.currentThread();
			System.out.println(t.getName()+":正在执行B方法");
			Thread.sleep(5000);
			System.out.println(t.getName()+":运行B方法完毕");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
  1. 将集合或Map转换为线程安全的
public class SyncDemo5 {

	public static void main(String[] args) {
		/*
		 * ArrayList与LinkedList都不是线程安全的
		 */
		List<String> list=new ArrayList<String>();
		list.add("one");
		list.add("two");
		list.add("three");
		list.add("four");
		System.out.println(list);
		//将给定集合转换为线程安全的集合
		list=Collections.synchronizedList(list);
		System.out.println(list);
		
		
		/*
		 * HashSet不是线程安全的
		 */
		Set<String> set=new HashSet<String>();
		System.out.println(set);
		//将给定的Set集合转换为线程安全的
		set=Collections.synchronizedSet(set);
		System.out.println(set);
		
		/*
		 * HashMap也不是线程安全的
		 */
		Map<String,Integer> map=new HashMap<String,Integer>();
		map.put("语文", 98);
		map.put("数学", 95);
		map.put("英语", 89);
		System.out.println(map);
		map=Collections.synchronizedMap(map);
		System.out.println(map);
		
		/*
		 * API手册上有说明,就算是线程安全的集合那么其中对于元素的操作,如add,remove等方法
		 * 都不与迭代器遍历做互斥,需要自行维护互斥关系
		 */
		

	}

}

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

	public static void main(String[] args) {
		ExecutorService threadPool=Executors.newFixedThreadPool(2);//创建线程池
		
		for(int i=0;i<5;i++){
			Runnable runn=new Runnable() {
				
				@Override
				public void run() {
					Thread t=Thread.currentThread();
					try {
						System.out.println(t+":正在运行任务");
						Thread.sleep(5000);
						System.out.println(t+"运行任务完毕");
					} catch (Exception e) {
						System.out.println("线程被中断");
					}
					
				}
			};
			threadPool.execute(runn);
			System.out.println("指派了一个任务交给线程池");	
		}
		threadPool.shutdownNow();
		System.out.println("停止线程池了");
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值