多线程拾遗-1

Java线程深入解析

1, 概念

概念(线程,进程,生命周期,并行与并发,用户线程与系统线程)
对象以及变量的并发访问,侧重于synchronize和lock。加一些volatile和threadlocal。
线程间的通信,以及一些实例
单例模式详解
查漏补缺

简单来所,进程是程序的一次运行,比如跑两个QQ,就是两个进程。而线程是更小的划分,是一个任务的执行。比如说QQ聊天时的一次视频。
- 进程
- 线程
- 线程的不安全性
- 线程常见的方法


* 进程

    /*
        待完成
    */

* 线程

    **线程是一次任务的执行,他在单核心的机器上只可以并发执行,就是说同一时间只会有一个线程占用cpu资源,这是因为,我们显式指定的线程,其实对操作系统而言是不可见的。这个线程任务的调度,其实jvm去调用系统线程来执行的,对用户来言是不可见的。**

在某些时候,我们可能需要指定某个线程到特定的cpu,比如将UI线程限制在一个cpu上,讲其他实时性较高的线程限制在另一个cpu上,这样当UI大量占用cpu时间的时候,也不会影响其他线程的执行。
一个进程中至少会有一个线程,即主线程,比如:

public class Test1{
    public static void main(String[] args){
        //会输出 main
        System.out.println(Thread.currentThread().getName());
    }
}

线程的实现

线程的实现主要有两种方式,一个是继承Thread类,另一个则是实现Runnable接口,还有一个Callable接口,会在别的地方拿出来和Runnable接口比较。**

public class MyThread extends Thread{
    @Override
    public void run(){}
}
public class MyThread implements Runnable{
    @Override
    public void run(){}
}
无论采用哪种方式,线程之间的执行都是随机的,任何时间都可能会让出cpu给别的线程。

线程的不安全

public class MyThread extends Thread{
    private int count = 5;
@Override
    public void run(){
            count--;
    System.out.println(“线程”+Thread.currentThread().getName()+ “ 计算后的count:” +count);
    }
}

测试类:
public class Run{
    public static void main(){
        MyThread mythread = new MyThread();
        new Thread(mythread,”A”).start();
        new Thread(mythread,”B”).start();
        new Thread(mythread,”C”).start();
        new Thread(mythread,”D”).start();
        new Thread(mythread,”E”).start();
        //输出结果可能不是5,4,3,2,1的顺序。
}
}
这是因为在jvm中,i—会分成3部分。
1,  取值
2,  计算i-1;
3,  赋值。
当多个线程同时访问的时候,可能会出现比如两个同时取值,同时计算,会导致后面的赋值覆盖前面的赋值,造成只执行了一次的假象。多次运行,会出现不同的情况。
当在run方法前面加上synchronize修饰时,结果会出现正常的5,4,3,2,1.这是因为synchronize修饰会导致线程的同步执行。

再举一个在学习web项目中经常出现的多线程登录出错的问题。

public class Test {
    public static void main(String[] args) {
        ALogin a = new ALogin();
        a.start();
        BLogin b = new BLogin();
        b.start();
    }
}

class LoginServlet{
    private static String usernameRef;
    private static String passwordRef;
    public static void doPost(String username,String password){
        try{
            usernameRef = username;
            if(username.equals("a")){
                Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("username = "+ usernameRef + " password= "+passwordRef);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class ALogin extends Thread{
    @Override
    public synchronized void start() {
        LoginServlet.doPost("a","aa");
    }
}
class BLogin extends Thread{
    @Override
    public synchronized void start() {
        LoginServlet.doPost("b","bb");
    }
}
多次运行Test类,会发现大概率会出现 
b aa
b bb
的情况,这是因为在a线程进行登录验证时发生了Thread.sleep(5000);导致b线程占用cpu并将usernameRef改为了b。同样,当加上synchronize关键字来修改doPost方法时,该问题便可以得到解决。

值得注意的是,System.out.println(i–)这个情况,虽然方法本身是线程安全的,但是i—在该方法执行前就进行的,所以依然是不安全的。

一些常见的方法:

Thread.currentThread()是一个静态方法,是一个native static方法,根据阿里规约,不要使用实例去调用static方法,要使用Thread.currentThread();

isAlive()方法,判断当前线程是否处于活动状态,System.out.println(thread.isAlive());不能作为判断该线程是否结束的依据,同理,thread.isAlive()是发生在输出方法之前的。

sleep()方法的作用是让线程在指定的毫秒数内休眠,即this.currentThread()返回的线程。
yield()方法的作用是让线程放弃cpu资源,然后进入就绪状态,有可能执行了yield方法后依然是该线程的cpu。

getId(),返回该线程的唯一标识。

停止线程(stop、suspend和resume 都是过期作废不安全的,不考虑使用,使用return来正确的停止一个线程)

interrupt()方法,可以理解为是为一个需要停止的线程打了“待停止”Tag,在别的地方可以通过查询这个tag来判断是否终止此线程。

Thread.interrupt()测试当前线程是否已经中断,该方法会在查询过后制终止状态为“未终止”,比如,对一个已经打了终止tag的线程执行两次interrupt()方法,第一次会返回true,而第二次会返回false,因为第一次查询过后已经清楚了该线程的待终止tag。
isInterrupted()测试线程是否已经中断。
这里要注意,如果在sleep睡眠中执行了interrupt方法,会报InterruptException异常,然后清除待终止tag。
在给线程打了带终止tag后,可以在线程内部即run()方法内建立逻辑判断,通过抛异常的方法来停止该线程。当然,更推荐的方式是return。

线程的优先级问题,默认为5,MIN_PRIORITY=1,MAX_PRIOORITY=10。线程的优先级只能保证大概率的情况下会优先执行优先级高的线程。比如windows中有一个优先级推进器,会把执行次数多的线程优先级提高。毕竟线程的最终的执行顺序还是由操作系统决定的。

线程优先级的继承性:
如果在A线程启动B线程,那么B线程会有和A线程一样的优先级。

守护线程:当线程中不存在非守护线程时,守护线程会自动销毁,比如GC垃圾回收器。
通过setDaemon(true);

<think>我们正在处理用户关于C++中将Unicode字符串(通常指宽字符字符串)转换为多字节字符串(如UTF-8或ANSI)的请求。根据引用资料,我们可以提供几种方法。用户提供的引用中:-引用[2]展示了多字节(ANSI)到Unicode(宽字符)的转换,但我们需要的是反向转换(Unicode到多字节)。-引用[4]展示了多字节到宽字符的转换,同样不是我们需要的方向,但提供了使用`mbstowcs_s`的示例,我们可以类比使用`wcstombs_s`。因此,我们需要提供Unicode(宽字符)到多字节的转换示例。这里我们假设用户需要的是转换为ANSI(本地代码页)或者UTF-8。注意,在Windows环境下,我们通常使用Win32API函数,如`WideCharToMultiByte`。此外,也可以使用C标准库函数(如`wcstombs`)或C++库(如`std::wstring_convert`,但注意C++11中已弃用,C++17中移除,所以可能不是最佳选择)。根据引用[4]中提供的思路,我们可以使用`wcstombs_s`(安全版本)或者Win32API的`WideCharToMultiByte`。下面提供几种方法:方法1:使用C标准库函数`wcstombs_s`(适用于ANSI,但要注意本地编码设置)方法2:使用Win32API函数`WideCharToMultiByte`(更灵活,可指定编码,如CP_ACP(ANSI)或CP_UTF8(UTF-8))方法3:使用C++标准库(但考虑到弃用,这里不推荐,不过如果需要跨平台且不使用弃用特性,可以使用第三方库如Boost或Poco)由于用户没有指定目标多字节编码,我们将提供两种常见情况:转换为ANSI(本地代码页)和转换为UTF-8。注意:转换过程中可能会丢失信息(如果目标编码不能表示所有字符),因此需要检查错误。示例1:使用`WideCharToMultiByte`转换为ANSI(本地代码页)示例2:使用`WideCharToMultiByte`转换为UTF-8示例3:使用C标准库`wcstombs_s`转换为本地编码(类似于ANSI)我们将编写三个示例函数。注意:在Windows环境下,我们通常使用`wchar_t`作为宽字符(UTF-16LE),而多字节字符串使用`char`。参考引用[2]和[4]中的函数形式,我们定义类似的函数。示例代码:</think>### C++ Unicode 转换为多字节示例代码 在C++中,Unicode(宽字符)与多字节字符之间的转换主要涉及以下两种场景: 1. **UTF-16 ↔ ANSI**(本地代码页) 2. **UTF-16 ↔ UTF-8** 以下是三种实现方式的代码示例: --- #### 方法1:使用 WinAPI `WideCharToMultiByte`(推荐) ```cpp #include <windows.h> #include <string> // Unicode (UTF-16) 转 ANSI (本地代码页) std::string UnicodeToANSI(const std::wstring& wstr) { int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); char* buffer = new char[len]; WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, buffer, len, NULL, NULL); std::string result(buffer); delete[] buffer; return result; } // Unicode (UTF-16) 转 UTF-8 std::string UnicodeToUTF8(const std::wstring& wstr) { int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); char* buffer = new char[len]; WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, buffer, len, NULL, NULL); std::string result(buffer); delete[] buffer; return result; } ``` **特点**: - 直接使用Windows API,效率高 - 支持错误处理(最后一个参数可指定替换字符) - 支持多种代码页(`CP_ACP`/`CP_UTF8`) --- #### 方法2:使用 C++11 `<locale>`(跨平台) ```cpp #include <locale> #include <codecvt> #include <string> // Unicode (UTF-16) 转 UTF-8 (C++11标准) std::string UnicodeToUTF8(const std::wstring& wstr) { std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; return converter.to_bytes(wstr); } // 注意:ANSI转换需指定本地化环境 std::string UnicodeToANSI(const std::wstring& wstr) { std::wstring_convert<std::codecvt_byname<wchar_t, char, std::mbstate_t>> converter(new std::codecvt_byname<wchar_t, char, std::mbstate_t>("")); return converter.to_bytes(wstr); } ``` **注意事项**: - `<codecvt>`在C++17中弃用,但在多数编译器仍可用 - ANSI转换需系统支持本地语言环境 --- #### 方法3:使用 C 标准库函数(基础实现) ```cpp #include <cstdlib> #include <string> #include <vector> std::string UnicodeToANSI(const std::wstring& wstr) { size_t len = wcstombs(nullptr, wstr.c_str(), 0) + 1; std::vector<char> buffer(len); wcstombs(buffer.data(), wstr.c_str(), len); return std::string(buffer.data()); } ``` **局限**: - 仅支持当前区域设置的ANSI编码 - 无法处理UTF-8转换 - 线程安全性较低 --- ### 关键概念说明 1. **编码类型** - **Unicode**:Windows中通常指`wchar_t`(UTF-16) - **多字节**: - ANSI:本地代码页(如GBK中文、Shift-JIS日文) - UTF-8:兼容ASCII的Unicode编码 2. **内存差异** ```mermaid graph LR Unicode字符串 -->|每个字符 2/4字节| wchar_t[] 多字节字符串 -->|变长编码| char[] ``` 3. **使用场景** - Windows API交互 → 优先用`WideCharToMultiByte` - 跨平台数据交换 → 使用UTF-8编码 - 旧版系统兼容 → ANSI编码 > **重要提示**:转换时需确保: > 1. 目标缓冲区足够大(通过预计算长度) > 2. 处理转换失败情况(如非法字符) > 3. ANSI编码有字符集限制,中文推荐UTF-8 [^1]: 字节序解析(附C++代码示例) [^2]: C++多字节与Unicode之间的转化 [^3]: C/C++语言拾遗(三)-多字节字符和Unicode [^4]: windows unicode和多字节字符集转换示例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值