您的位置:首页 > 编程语言 > Java开发

线程相关

2016-04-11 00:00 302 查看
摘要: 线程的两种创建方式及其优缺点;获取线程属性信息的相关API;守护线程;线程池;多线程并发安全问题;互斥锁;将非线程安全的集合转化为线程安全的方法;

/**
* 线程创建有两种方式
* 方式一: 继承Thread并重写run方法
* @author Romanceling
*
*/
public class ThreadTest1 {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
/*
* 启动线程应当使用线程的start方法,而不要直接调用run方法.
* start方法会快速的执行完毕,作用是将线程纳入线程调度,使其具有并发执行的能力
* 一旦线程获取CPU时间片开始运行时,会自动调用自己的run方法.
* 需要注意,在概念上不能理解为调用start方法时run方法被执行.
* 而是start方法执行完毕后,该线程的run方法会很快的被执行(一旦获取时间片)
*/
t1.start();
t2.start();
}
}
/**
* 方式一创建线程的优点:
* 定义简单,适合匿名内部类快速创建线程
* 缺点:
* 1:由于java是单继承的,这就导致继承了Thread就不能再继承其他类,在项目中扩展性差.
* 2:由于将线程执行的任务定义在线程的run方法中,导致线程与任务有一个强耦合关系.
* 不利于线程重用.
*/
class MyThread1 extends Thread{
/**
* run方法用来定义线程要执行的任务
*/
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 ThreadTest2 {
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("我是修水管的!");
}
}
}

/**
* 使用匿名内部类的形式创建线程
*/
public class ThreadTest3 {
public static void main(String[] args) {
//方式一
Thread t1 = new Thread(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊?");
}
}
};

//方式二
Runnable runn = new Runnable(){
public void run(){
for(int i=0;i<1000;i++){
System.out.println("我是查水表的!");
}
}
};
Thread t2 = new Thread(runn);

t1.start();
t2.start();
}
}
/**
* 获取线程属性信息的相关API
*/
public class ThreadTest4 {
public static void main(String[] args) {
/**
* 线程提供了一个静态方法
* static Thread currentThread()
* 该方法可以获取运行当前方法的线程
*/
//获取运行当前方法(main)的线程
Thread t = Thread.currentThread();
System.out.println("运行main方法的线程是:"+t);

//获取ID
long id = t.getId();
System.out.println("id:"+id);

//获取名字
String name = t.getName();
System.out.println("name:"+name);

//获取线程优先级
int priority = t.getPriority();
System.out.println("优先级:"+priority);

/**
* 线程优先级:
* 线程不能干涉线程调度的工作.线程不能要时间片,只能被动的被分配,
* 并且分配给哪个线程也是不可控的.改变线程的优先级可以改变时间片分配几率.
* 理论上,线程优先级高的线程获取CPU时间片的次数多.
*/
t.setPriority(Thread.MAX_PRIORITY);

//线程是否存活
boolean isAlive = t.isAlive();
System.out.println("isAlive:"+isAlive);

//是否是守护线程
boolean isDaemon = t.isDaemon();
System.out.println("isDaemon:"+isDaemon);

//是否被中断
boolean isInter = t.isInterrupted();
System.out.println("isInterrupted:"+isInter);

/**
* 线程提供了静态方法:
* static void sleep(long ms)
* 该方法会阻塞运行当前方法的线程指定毫秒.当指定时间经过后,
* 线程会重新回到runnable状态,等待分配时间片并发运行.
*/
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
/**
* 用sleep静态方法实现电子表功能
* 每秒钟在控制台输出当前系统时间
* 格式:15:17:08
*/
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss" );
while(true){
System.out.println(sdf.format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}

/**
* 线程提供了静态方法:
* static void yield()
* 使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
*/

/**
* 线程提供了方法:
* void join()
* 该方法用于等待当前线程结束。此方法是一个阻塞方法。
* 该方法声明抛出InterruptException。
*/
public static void main(String[] args) {
final Thread t1 = new Thread(){
public void run(){
//一些耗时的操作
}
};

Thread t2 = new Thread(){
public void run(){
try {
t1.join();//这里t2线程会开始阻塞,
直到t1线程的run方法执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
//以下是当前线程的任务代码,只有t1线程运行完毕才会运行。
}
};
}
}
}

/**
* 守护线程
* 当一个进程中的所有前台线程都结束时,进程结束,
* 无论后台线程(守护线程)是否还在运行都要强制中断.
*/
public class ThreadTest5 {
public static void main(String[] args) {

/*
* rose:扮演者为前台线程
*/
final 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:啊啊啊啊啊AAAAAaaaaa....");
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) {
}
}
}
};
//设置为后台线程
jack.setDaemon(true);
rose.start();
jack.start();

}
}
/**
* 线程池
* 池的概念:
* 池通常用于储存多个相同内容的元素,使用池可以控制元素数量,同时重用元素。
* 对于元素的创建与销毁全部由池来完成的。
*
* 线程池:
* 控制线程数量,并重用线程。
*/
public class ThreadPoolTest {
public static void main(String[] args) {
//创建一个固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++){
Runnable runn = new Runnable(){
public void run(){
Thread t = Thread.currentThread();
System.out.println(t+":正在执行任务。。");
try{
Thread.sleep(5000);
}catch(InterruptedException e){

}
System.out.println(t+":执行完毕!");
}
};
//将任务交给线程池
threadPool.execute(runn);
System.out.println("将任务交给了线程池!");
}
/*
* 停止线程池有两个方法:
* 使用shutdown()方法停止线程,那么线程池会现将当前线程池中所有任务
* 执行完毕后停止。
* 使用shutdownNow()方法停止线程,那么线程池会立即停止。
*/
threadPool.shutdown();
System.out.println("关闭了线程池。");
}
}
/**
* 多线程并发安全问题
* 当多个线程操作同一资源时,由于线程切换的不确定性,会导致出现线程安全问题.
*/
public class SyncTest1 {
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{
private int beans = 20;
/**
* 当一个方法被synchronized修饰后,当前方法称为"同步方法",
* 多个线程不能同时进入方法内部执行,只能"排队执行".
* 要想解决多线程并发安全问题,就需要将执行代码
* 从"异步"(各干各的)变为"同步"(排队干)执行.
* 当在成员方法上使用synchronized,上锁的对象
* 就是当前方法所属对象,即方法中看到的"this".
*/
public synchronized int getBean(){
if(beans==0){
throw new RuntimeException("没有豆子了!");
}
Thread.yield();//模拟到这里发生线程切换
return beans--;
}
}
/**
* 有效缩小同步范围可以提高线程并发效率
*/
public class SyncTest2 {
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(){
try{
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在挑选衣服...");
Thread.sleep(5000);
/*
* 同步块可以有效的缩小同步范围.同步块需要指定"同步监视器",
* 即上锁的对象。只要保证多个线程看到的该对象是同一个,
* 那么这些线程在执行其中代码时就具有"同步"效果,
* 只能排队执行.通常使用this即可.
*/
synchronized(this){
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开");

}catch(Exception e){
}
}
}
/**
* 静态方法同步
*/
public class SyncTest3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run(){
SyncDemo3.dosome();
}
};
Thread t2 = new Thread(){
public void run(){
SyncDemo3.dosome();
}
};
t1.start();
t2.start();
}
/**
* 静态方法被synchronized修饰后,一定具有同步效果.
* 静态方法的上锁对象为当前类的类对象,java中有一个名为Class的类.
* 其每一个实例用于描述java中的一个类.每当我们使用一个类时,
* JVM在读取该类class文件后都会创建一个Class的实例来保存该类的信息
* 以便使用.所以在JVM内部,每一个类都有一个且只有一个Class的实例相对应
* 静态方法就是给这个对象上锁的.
*/
public synchronized static void dosome(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在运行dosome方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t.getName()+"运行dosome方法完毕!");
}
}
/**
* synchronized互斥锁的效果
* 当synchronized将不同的代码片段修饰后,只要锁对象相同,
* 那么这些代码是互斥的.多个线程不能同时进到这些不同方法中
*/
public class SyncTest4 {
public static void main(String[] args) {
final 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 void methodA(){
synchronized(this){
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在运行methodA方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t.getName()+"运行methodA方法完毕");
}
}

public void methodB(){
synchronized(this){
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在运行methodB方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t.getName()+"运行methodB方法完毕");
}
}
}
/**
* 将非线程安全的集合转化为线程安全的方法。
*
* List集合常用API
* ArrayList ,LinkedList
*
* Set集合常用API
* HashSet
*
* Map常用API
* HashMap
* 以上常用API都不是线程安全的。
*/
public class SynAPITest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("three");
//将当前集合转化为线程安全的。
list = Collections.synchronizedList(list);
System.out.println(list);

//Set集合不会有重复元素。无序的
Set<String> set = new HashSet<String>(list);
//将给定的Set集合转化成一个线程安全的。
set = Collections.synchronizedSet(set);
System.out.println(set);

//无序,后输入的相同的key的value会替换先输入的value
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("语文", 10);
map.put("数学", 20);
map.put("模电", 0);
map.put("模电", 10);

//将map转换为线程安全的
map = Collections.synchronizedMap(map);
System.out.println(map);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 线程